const {checkType} = require("./Utility");
const {PipelineStage} = require("./PipelineStage");
const pipelineMap = new Map();
/**
* A structure for bulding and reconfiguring algorithms. This is meant to
* accelerate experimentation by creating re-usable and reconfigurable chunks
* of code. When an algorithm is finalized this can be optimized out by
* replacing the Pipeline with a suitable function that performs the same algorithm.
*/
class Pipeline {
/**
* constructor
*
* @param {string} name The name of the Pipeline
* @returns {Pipeline} The Pipeline that was created
*/
constructor(name) {
checkType("Pipeline.constructor", "name", name, "string");
this.name = name;
this.firstStage = null;
}
/**
* Execute the pipeline
*
* @param {any} input Input data value for the first stage of the pipeline
* @returns {Promise<any>} A Promise that resolves to the value of the pipeline, or rejects on error
*/
async run(input) {
return this.firstStage.run(input);
}
/**
* Builds the pipeline from a description and assigns it to the first stage of this pipeline. Pipeline
* building is done using the static `build` method.
*
* @param {Array<string>} desc An array of pipeline stage names or objects describing each stage.
*/
build(desc) {
this.firstStage = Pipeline.build(desc);
}
// TODO: toString() {}
/**
* Builds a pipeline based on a description of the pipeline. The pipeline is an Array of pipeline stage objects.
*
* @param {Array<string|Object>} desc The description of the pipeline. See the test file `pipeline.js` for examples
* @returns {PipelineStage} The first stage of the built pipeline.
*/
static build(desc) {
if (typeof desc === "string") {
return PipelineStage.create(desc);
}
if (typeof desc === "object" && !Array.isArray(desc)) {
let keys = Object.keys(desc);
// must only be one key
if (keys.length !== 1) {
throw new TypeError(`Pipeline.build: expected description object to have exactly one key, got: ${keys}`);
}
let name = keys[0];
// serial is a special key
if (name === "serial") {
return Pipeline.build(desc.serial);
}
return PipelineStage.create(name, desc[name]);
}
if (!Array.isArray(desc)) {
throw new TypeError();
}
// convert all stage descriptions to PipelineStage objects
let stages = desc.map((item) => {
return Pipeline.build(item);
});
// link the stages together
stages.reduce((thisStage, nextStage) => {
thisStage.setOutput(nextStage);
return nextStage;
});
// save the first stage
this.firstStage = stages[0];
return this.firstStage;
}
/**
* Creates a new pipleine named `name` built to the description `desc`
*
* @param {string} name The name of the pipeline, for future reference
* @param {Array<string|Object>} desc The pipeline description passed to `build`
* @returns {Pipeline} The newly created Pipeline
*/
static create(name, desc) {
checkType("Pipeline.create", "name", name, "string");
let p = new Pipeline(name);
p.build(desc);
pipelineMap.set(name, p);
return p;
}
/**
* Retrieves a pipeline with the matching name
*
* @param {string} name Name of the pipeline to return
* @returns {Pipeline} The matching Pipeline or undefined if no matching Pipeline is found
*/
static get(name) {
return pipelineMap.get(name);
}
}
module.exports = {
Pipeline,
};