59 lines
1.6 KiB
JavaScript
59 lines
1.6 KiB
JavaScript
const trimStart = (s, c) => {
|
|
let idx = 0
|
|
while (idx < s.length && s[idx] === c)
|
|
idx++
|
|
return idx > 0 ? s.substring(idx) : s
|
|
}
|
|
|
|
const optkey = (opt) => trimStart(opt, '-')
|
|
|
|
const parse = (default_options, option_arities) => (args) => {
|
|
const positional = []
|
|
const parsed_options = {}
|
|
|
|
for (let i = 0; i < args.length;) {
|
|
const arg = args[i]
|
|
i++
|
|
|
|
const arity = option_arities[arg]
|
|
if (typeof(arity) === 'number') {
|
|
if (arity+i > args.length)
|
|
return [`${arg}: not enough arguments.`, parsed_options, positional]
|
|
|
|
const opt = optkey(arg)
|
|
switch (arity) {
|
|
case 0: parsed_options[opt] = true; break
|
|
case 1: parsed_options[opt] = args[i]; break
|
|
default: parsed_options[opt] = args.slice(i, i+arity); break
|
|
}
|
|
i += arity
|
|
} else {
|
|
positional.push(arg)
|
|
}
|
|
}
|
|
|
|
const options = Object.assign({}, default_options, parsed_options)
|
|
return [null, options, positional]
|
|
}
|
|
|
|
const help = ({ grammar, usage }) => () => {
|
|
if (usage) console.log(usage)
|
|
grammar.forEach(
|
|
([optargs, optdesc, def]) => {
|
|
const deftext = def ? ` (default: ${def})` : ""
|
|
console.log(`\t${optargs.join(' ')}\t${optdesc}${deftext}`)
|
|
}
|
|
)
|
|
}
|
|
|
|
const CLI = ({ grammar, usage }) => {
|
|
const details = grammar.map(([[opt, ...optargs], optdesc, def]) => [opt, optargs.length, def])
|
|
const option_arities = Object.fromEntries(details.map(([opt, arity, _def]) => [opt, arity]))
|
|
const default_options = Object.fromEntries(details.map(([opt, _arity, def]) => [optkey(opt), def]))
|
|
return {
|
|
parse: parse(default_options, option_arities),
|
|
help: help({ grammar, usage }),
|
|
}
|
|
}
|
|
|
|
module.exports = CLI
|