decline-ts
Version:
Composable command-line parser for TypeScript - a (partial) porting of Scala decline using fp-ts
93 lines (92 loc) • 5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Help = void 0;
const fp_ts_1 = require("fp-ts");
const function_1 = require("fp-ts/function");
const Opts_1 = require("./Opts");
const Usage_1 = require("./Usage");
const StringUtils_1 = require("./utils/StringUtils");
var Help;
(function (Help) {
/**
* Constructors
*/
Help.fromCommand = (parser) => {
const commands = commandList(parser.opts);
const commandHelp = fp_ts_1.readonlyArray.isEmpty(commands)
? fp_ts_1.readonlyArray.empty
: function_1.pipe(commands, fp_ts_1.readonlyArray.chain(command => [
withIndent(4, command.name),
withIndent(8, command.header),
]), texts => function_1.pipe(fp_ts_1.readonlyArray.cons('Subcommands:', texts), StringUtils_1.StringUtils.mkString('\n'), fp_ts_1.readonlyArray.of));
const optionsDetail = detail(parser.opts);
const optionsHelp = fp_ts_1.readonlyArray.isEmpty(optionsDetail)
? fp_ts_1.readonlyArray.empty
: function_1.pipe(fp_ts_1.readonlyArray.cons('Options:', optionsDetail), StringUtils_1.StringUtils.mkString('\n'), fp_ts_1.readonlyArray.of);
return {
errors: fp_ts_1.readonlyArray.empty,
prefix: fp_ts_1.readonlyNonEmptyArray.of(parser.name),
usage: function_1.pipe(parser.opts, Usage_1.Usage.fromOpts, fp_ts_1.readonlyArray.chain(Usage_1.Usage.show)),
body: fp_ts_1.readonlyArray.cons(parser.header, [...optionsHelp, ...commandHelp]),
};
};
/**
* Methods
*/
Help.withErrors = (moreErrors) => (help) => (Object.assign(Object.assign({}, help), { errors: [...help.errors, ...moreErrors] }));
Help.withPrefix = (prefix) => (help) => (Object.assign(Object.assign({}, help), { prefix: function_1.pipe(prefix, fp_ts_1.readonlyArray.reduceRight(help.prefix, fp_ts_1.readonlyNonEmptyArray.cons)) }));
Help.stringify = (help) => {
const maybeErrors = fp_ts_1.readonlyArray.isEmpty(help.errors)
? fp_ts_1.readonlyArray.empty
: function_1.pipe(help.errors, StringUtils_1.StringUtils.mkString('\n'), fp_ts_1.readonlyArray.of);
const prefixString = function_1.pipe(help.prefix, StringUtils_1.StringUtils.mkString(' '));
const usageString = fp_ts_1.readonlyArray.isEmpty(help.usage)
? `Usage: ${prefixString}`
: help.usage.length === 1
? `Usage: ${prefixString} ${help.usage[0]}` // TODO: cast bad.
: function_1.pipe(fp_ts_1.readonlyArray.cons('Usage:', help.usage), StringUtils_1.StringUtils.mkString(`\n ${prefixString} `));
return function_1.pipe([...maybeErrors, ...fp_ts_1.readonlyArray.cons(usageString, help.body)], StringUtils_1.StringUtils.mkString('\n\n'));
};
})(Help = exports.Help || (exports.Help = {}));
const optionList = (opts) => {
switch (opts._tag) {
case 'Pure':
return fp_ts_1.option.some(fp_ts_1.readonlyArray.empty);
case 'App':
return function_1.pipe(fp_ts_1.apply.sequenceT(fp_ts_1.option.option)(optionList(opts.f), optionList(opts.a)), fp_ts_1.option.map(([a, b]) => [...a, ...b]));
case 'OrElse':
const b = optionList(opts.b);
return function_1.pipe(optionList(opts.a), fp_ts_1.option.map(a => function_1.pipe(b, fp_ts_1.option.fold(() => a, _ => [...a, ..._]))), fp_ts_1.option.alt(() => b));
case 'Single':
return fp_ts_1.option.some(fp_ts_1.readonlyArray.of([opts.opt, false]));
case 'Repeated':
return fp_ts_1.option.some(fp_ts_1.readonlyArray.of([opts.opt, true]));
case 'Subcommand':
return fp_ts_1.option.some(fp_ts_1.readonlyArray.empty);
case 'Validate':
return optionList(opts.value);
case 'HelpFlag':
return optionList(opts.flag);
}
};
const commandList = (opts) => {
switch (opts._tag) {
case 'App':
return [...commandList(opts.f), ...commandList(opts.a)];
case 'OrElse':
return [...commandList(opts.a), ...commandList(opts.b)];
case 'Subcommand':
return fp_ts_1.readonlyArray.of(opts.command);
case 'Validate':
return commandList(opts.value);
default:
return fp_ts_1.readonlyArray.empty;
}
};
const getOptBooleanTupleEq = () => fp_ts_1.eq.getTupleEq(Opts_1.Opts.Opt.eq(), fp_ts_1.eq.eqBoolean);
const detail = (opts) => function_1.pipe(optionList(opts), fp_ts_1.option.getOrElse(() => fp_ts_1.readonlyArray.empty), fp_ts_1.readonlyArray.uniq(getOptBooleanTupleEq()), fp_ts_1.readonlyArray.chain(
// ([opt, _]) =>
// if (opt._tag === 'Regular') TODO
// if (opt._tag === 'Flag') TODO
() => fp_ts_1.readonlyArray.empty));
const withIndent = (indent, str) => function_1.pipe(str.split('\n'), fp_ts_1.readonlyArray.map(_ => `${' '.repeat(indent)}${_}`), StringUtils_1.StringUtils.mkString('\n'));