Initial commit
commit
f4004c035d
@ -0,0 +1 @@
|
||||
node_modules
|
@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
|
||||
const Promise = require("bluebird");
|
||||
const path = require("path");
|
||||
const errorChain = require("error-chain");
|
||||
|
||||
const pipe = require("@promistream/pipe");
|
||||
const fromNodeStream = require("@promistream/from-node-stream");
|
||||
const map = require("@promistream/map");
|
||||
const join = require("@promistream/join");
|
||||
const splitLines = require("@promistream/split-lines");
|
||||
const decodeString = require("@promistream/decode-string");
|
||||
const readFile = require("./");
|
||||
|
||||
return Promise.try(() => {
|
||||
return pipe([
|
||||
readFile(path.join(__dirname, "example.js")),
|
||||
decodeString("utf8"),
|
||||
splitLines(),
|
||||
map((line) => line.trim()),
|
||||
join("\n"),
|
||||
fromNodeStream(process.stdout)
|
||||
]).read();
|
||||
}).then(() => {
|
||||
console.log("Done!");
|
||||
}).catch((error) => {
|
||||
console.error(errorChain.render(error));
|
||||
});
|
@ -0,0 +1,78 @@
|
||||
"use strict";
|
||||
|
||||
const Promise = require("bluebird");
|
||||
const fs = require("fs").promises;
|
||||
|
||||
const pipe = require("@promistream/pipe");
|
||||
const simpleSource = require("@promistream/simple-source");
|
||||
const sequentialize = require("@promistream/sequentialize");
|
||||
const EndOfStream = require("@promistream/end-of-stream");
|
||||
|
||||
const { validateArguments } = require("@validatem/core");
|
||||
const required = require("@validatem/required");
|
||||
const isString = require("@validatem/is-string");
|
||||
const isNumber = require("@validatem/is-number");
|
||||
const isInteger = require("@validatem/is-integer");
|
||||
const defaultTo = require("@validatem/default-to");
|
||||
const either = require("@validatem/either");
|
||||
const oneOf = require("@validatem/one-of");
|
||||
|
||||
// NOTE: Below read size is derived from the high water mark in core lib/internal/fs/streams.js
|
||||
let defaultChunkSize = 64 * 1024;
|
||||
|
||||
function doRead(handle, length) {
|
||||
// TODO: Can we safely switch to `allocUnsafe` here?
|
||||
let buffer = Buffer.alloc(length);
|
||||
|
||||
return Promise.try(() => {
|
||||
return handle.read(buffer, 0, length);
|
||||
}).then((result) => {
|
||||
if (result.bytesRead === 0) {
|
||||
// Not documented; https://github.com/nodejs/node/issues/35363
|
||||
throw new EndOfStream();
|
||||
} else if (result.bytesRead === length) {
|
||||
return buffer;
|
||||
} else if (result.bytesRead < length) {
|
||||
// TODO: For possible future performance optimization, consider reusing the remaining Buffer allocation for a next read, if possible. Need more data on how often this case occurs first, though, to justify the added complexity.
|
||||
return buffer.slice(0, length);
|
||||
} else {
|
||||
throw new Error(`Read more bytes (${result.bytesRead}) than the specified 'length' (${length}); this should never happen!`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function createReadFileStream(_path, _options) {
|
||||
let [ path, options ] = validateArguments(arguments, [
|
||||
[ "path", required, isString ],
|
||||
[ "options", defaultTo({}), {
|
||||
chunkSize: [ isInteger, defaultTo(defaultChunkSize) ],
|
||||
flag: [ defaultTo("r"), oneOf([ "a", "ax", "a+", "ax+", "as", "as+", "r", "r+", "rs+", "w", "wx", "w+", "wx+" ]) ],
|
||||
mode: [ either([ isString, isNumber ]) ] // FIXME: Stricter validation
|
||||
// TODO: start/end
|
||||
}]
|
||||
]);
|
||||
|
||||
let handlePromise = fs.open(path, options.flag, options.mode);
|
||||
|
||||
// TODO: Metadata, including stream label and file size/type/path
|
||||
return pipe([
|
||||
simpleSource({
|
||||
onRequest: () => {
|
||||
return Promise.try(() => {
|
||||
// TODO: Can we optimize this by separately tracking when the open has completed, and replacing the Promise with the actual handle in that case?
|
||||
return handlePromise;
|
||||
}).then((handle) => {
|
||||
return doRead(handle, options.chunkSize);
|
||||
});
|
||||
},
|
||||
onAbort: (_reason) => {
|
||||
return Promise.try(() => {
|
||||
return handlePromise;
|
||||
}).then((handle) => {
|
||||
return handle.close();
|
||||
});
|
||||
}
|
||||
}),
|
||||
sequentialize()
|
||||
]);
|
||||
};
|
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@promistream/read-file",
|
||||
"version": "0.1.0",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
"promistream"
|
||||
],
|
||||
"repository": "http://git.cryto.net/promistream/read-file.git",
|
||||
"author": "Sven Slootweg <admin@cryto.net>",
|
||||
"license": "WTFPL OR CC0-1.0",
|
||||
"dependencies": {
|
||||
"@promistream/end-of-stream": "^0.1.0",
|
||||
"@promistream/pipe": "^0.1.1",
|
||||
"@promistream/sequentialize": "^0.1.0",
|
||||
"@promistream/simple-source": "^0.1.1",
|
||||
"@validatem/core": "^0.3.11",
|
||||
"@validatem/default-to": "^0.1.0",
|
||||
"@validatem/either": "^0.1.9",
|
||||
"@validatem/error": "^1.1.0",
|
||||
"@validatem/is-integer": "^0.1.0",
|
||||
"@validatem/is-number": "^0.1.3",
|
||||
"@validatem/is-string": "^0.1.1",
|
||||
"@validatem/one-of": "^0.1.1",
|
||||
"@validatem/required": "^0.1.1",
|
||||
"bluebird": "^3.7.2",
|
||||
"default-value": "^1.0.0",
|
||||
"validatem": "^0.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@joepie91/eslint-config": "^1.1.0",
|
||||
"@promistream/decode-string": "^0.1.0",
|
||||
"@promistream/from-node-stream": "^0.1.1",
|
||||
"@promistream/join": "^0.1.0",
|
||||
"@promistream/map": "^0.1.0",
|
||||
"@promistream/split-lines": "^0.1.0",
|
||||
"error-chain": "^0.1.0",
|
||||
"eslint": "^6.8.0"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue