const fnParams = require('../fn-params.js');
/**
* @module pete/lib/runs/auto
*/
module.exports = createRunAuto;
/**
* Map of supported function runners.
* Every key is a "type name", every value is a factory function.
*
* @alias module:pete/lib/runs/auto.TYPES
* @readonly
* @constant
* @enum {Function}
*/
const TYPES = module.exports.TYPES = {
/* eslint-disable global-require */
/**
* @type {module:pete/lib/runs/sync}
*/
sync : require('./sync.js'),
/**
* @type {module:pete/lib/runs/generator}
*/
generator: require('./generator.js'),
/**
* @type {module:pete/lib/runs/callback}
*/
callback : require('./callback.js'),
/**
* @type {module:pete/lib/runs/async}
*/
async : require('./async.js'),
/**
* @type {module:pete/lib/runs/fork}
*/
fork : require('./fork.js'),
/**
* @type {module:pete/lib/runs/skip}
*/
skip : require('./skip.js'),
/**
* @type {module:pete/lib/runs/echo}
*/
echo : require('./echo.js')
/* eslint-enable global-require */
};
/**
* Try to find a best way to run target function.
* If `fn` is a string, create and return a `fork` run.
* If it is an `async` function, or it runs and return a Promise or an object with a `catch` method, create and return an `async` run.
* If it can be parsed and last argument is named "cb", "callback", "done", or "next", create and return a `callback` run.
* Otherwise create and return a `sync` run.
*
* @alias module:pete/lib/runs/auto
* @param {Function} fn
* @param {object} options
* @param {object} state
* @return {module:pete/lib/runner~run}
*/
function createRunAuto (fn, options, state) {
if (typeof fn === 'string') {
if (fn.endsWith('.json')) {
return TYPES.echo(fn, options, state);
}
return TYPES.fork(fn, options, state);
}
const code = fn.toString();
if (code.startsWith('async')) {
return TYPES.async(fn, options, state);
}
else if (code.match(/^function[\s\n]*[*]/)) {
return TYPES.generator(fn, options, state);
}
var params;
try {
params = fnParams(code);
}
catch (e) {
// Unknown, so return function that just errors out.
return function unknown (report) {
report(e);
report();
};
}
if (!params || params.length < 1) {
return tryRun(fn, options, state);
}
if (params.pop().match(/^cb|callback|done|next$/)) {
return TYPES.callback(fn, options, state);
}
return TYPES.sync(fn, options, state);
}
/**
* Run function.
* If it returns an object with a `catch` function, create and return an `async` run.
* If it throws an error, or returns anything else, create and return a `sync` run.
*
* @private
* @param {Function} fn
* @param {object} options
* @param {object} state
* @return {module:pete/lib/runner~run}
*/
function tryRun (fn, options, state) {
var temp = null;
try {
temp = fn();
}
catch (e) { // eslint-disable-line no-unused-vars
return TYPES.sync(fn, options, state);
}
if (temp && typeof temp.catch === 'function') {
temp.catch(() => { /* Ignore */ });
return TYPES.async(fn, options, state);
}
return TYPES.sync(fn, options, state);
}