Skip to content

Commit

Permalink
Use dedicated error class for End-Of-Stream Error
Browse files Browse the repository at this point in the history
  • Loading branch information
Borewit committed Dec 24, 2019
1 parent 0f8407b commit d2ec30b
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 45 deletions.
87 changes: 53 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,66 +17,85 @@ similar as you would read from a file.

## Usage

### Installation

```shell script
npm install --save then-read-stream
```

The `then-read-stream` contains one class: `StreamReader`, which reads from a [stream.Readable](https://nodejs.org/api/stream.html#stream_class_stream_readable).

### Compatibility

NPM module is compliant with [ECMAScript 2017 (ES8)](https://en.wikipedia.org/wiki/ECMAScript#8th_Edition_-_ECMAScript_2017).

#### Examples
## Examples

In the following example we read the first 16 bytes from a stream and store them in our buffer.
Source code of examples can be found [here](test/examples.ts).

```js
import * as fs from 'fs';
import { StreamReader } from 'then-read-stream';
const fs = require('fs');
const { StreamReader } = require('then-read-stream');

const readable = fs.createReadStream('JPEG_example_JPG_RIP_001.jpg');
const streamReader = new StreamReader(readable);
(async () => {

const buffer = Buffer.alloc(16);
const fileReadStream = fs.createReadStream('JPEG_example_JPG_RIP_001.jpg');
const streamReader = new StreamReader(fileReadStream);
const buffer = Buffer.alloc(16);

const bytesRead = await streamReader.read(buffer, 0, 16);
// buffer contains 16 bytes, if the end-of-stream has not been reached
})();
```

return streamReader.read(buffer, 0, 16)
.then( bytesRead => {
// buf, contains bytesRead, which will be 16 if the end-of-stream has not been reached
});
End-of-stream detection:
```js
(async () => {

const fileReadStream = fs.createReadStream('JPEG_example_JPG_RIP_001.jpg');
const streamReader = new StreamReader(fileReadStream);
const buffer = Buffer.alloc(16);

try {
await streamReader.read(buffer, 0, 16);
// buffer contains 16 bytes, if the end-of-stream has not been reached
} catch(error) {
if (error instanceof EndOfStreamError) {
console.log('End-of-stream reached');
}
}
})();
```

With peek you can read ahead:
```js
import * as fs from 'fs';
import { StreamReader } from 'then-read-stream';
const fs = require('fs');
const { StreamReader } = require('then-read-stream');

const fileReadStream = fs.createReadStream('JPEG_example_JPG_RIP_001.jpg');
const streamReader = new StreamReader(fileReadStream);
const buffer = Buffer.alloc(20);

return streamReader.peek(buffer, 0, 3)
.then(bytesRead => {
if (bytesRead === 3 && buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) {
console.log('This is a JPEG file');
return streamReader.read(buffer, 0, 20); // Read JPEG header
} else {
throw Error('Expected a JPEG file');
}
})
.then(bytesRead => {
if (bytesRead === 20) {
console.log('Got the JPEG header');
} else {
throw Error('Failed to read JPEG header');
}
});
(async () => {
let bytesRead = await streamReader.peek(buffer, 0, 3);
if (bytesRead === 3 && buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) {
console.log('This is a JPEG file');
} else {
throw Error('Expected a JPEG file');
}

bytesRead = await streamReader.read(buffer, 0, 20); // Read JPEG header
if (bytesRead === 20) {
console.log('Got the JPEG header');
} else {
throw Error('Failed to read JPEG header');
}
})();
```

If you have to skip a part of the data, you can use ignore:
```js
return streamReader.ignore(16)
.then( bytesIgnored => {
if (bytesIgnored < 16){
console.log(`Remaining stream length was ${bytesIgnored}, expected 16`);
}
});
await streamReader.ignore(16);
```

11 changes: 11 additions & 0 deletions lib/EndOfFileStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const defaultMessages = 'End-Of-Stream';

/**
* Thrown on read operation of the end of file or stream has been reached
*/
export class EndOfStreamError extends Error {

constructor() {
super(defaultMessages);
}
}
11 changes: 4 additions & 7 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as assert from 'assert';
import * as stream from 'stream';
import { EndOfStreamError } from './EndOfFileStream';
export { EndOfStreamError } from './EndOfFileStream';

interface IReadRequest {
buffer: Buffer | Uint8Array,
Expand All @@ -25,11 +27,6 @@ class Deferred<T> {

const maxStreamReadSize = 1 * 1024 * 1024; // Maximum request length on read-stream operation

/**
* Error message
*/
export const endOfStream = 'End-Of-Stream';

export class StreamReader {

/**
Expand All @@ -49,7 +46,7 @@ export class StreamReader {
if (!s.read || !s.once) {
throw new Error('Expected an instance of stream.Readable');
}
this.s.once('end', () => this.reject(new Error(endOfStream)));
this.s.once('end', () => this.reject(new EndOfStreamError()));
this.s.once('error', err => this.reject(err));
this.s.once('close', () => this.reject(new Error('Stream closed')));
}
Expand Down Expand Up @@ -81,7 +78,7 @@ export class StreamReader {
}

if (this.peekQueue.length === 0 && this.endOfStream) {
throw new Error(endOfStream);
throw new EndOfStreamError();
}

let remaining = length;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"url": "git+https://github.com/Borewit/then-read-stream"
},
"license": "MIT",
"main": "./lib/index",
"main": "lib/index.js",
"typings": "lib/index",
"bugs": {
"url": "https://github.com/Borewit/then-read-stream/issues"
Expand Down
4 changes: 2 additions & 2 deletions test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { EventEmitter } from 'events';
import * as fs from 'fs';
import * as Path from 'path';
import { Readable } from 'stream';
import { endOfStream, StreamReader } from '../lib';
import { EndOfStreamError, StreamReader } from '../lib';
import { SourceStream } from './util';

describe('StreamReader', () => {
Expand Down Expand Up @@ -58,7 +58,7 @@ describe('StreamReader', () => {
await streamReader.read(buf, 0, 1);
assert.fail('Should reject due to end-of-stream');
} catch (err) {
assert.equal(err.message, endOfStream);
assert.instanceOf(err, EndOfStreamError);
}
});
});
Expand Down
2 changes: 1 addition & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@
},
"rulesDirectory": [
]
}
}

0 comments on commit d2ec30b

Please sign in to comment.