UNPKG

@eeue56/baner

Version:

Flag parsing library in Typescript

422 lines (420 loc) 10.2 kB
// src/types.ts function Ok(value) { return { kind: "Ok", value }; } function Err(error) { return { kind: "Err", error }; } function string() { return { kind: "StringArgument" }; } function number() { return { kind: "NumberArgument" }; } function boolean() { return { kind: "BooleanArgument" }; } function empty() { return { kind: "BooleanArgument" }; } function list(flagArgumentParsers) { return { kind: "ListArgument", items: flagArgumentParsers }; } function VariableListArgument(item) { return { kind: "VariableListArgument", item }; } function variableList(flagArgumentParser) { return VariableListArgument(flagArgumentParser); } function oneOf(items) { return { kind: "OneOfArgument", items }; } function shortFlag(name, help2, parser2) { return { kind: "Short", name, help: help2, parser: parser2 }; } function longFlag(name, help2, parser2) { return { kind: "Long", name, parser: parser2, help: help2 }; } function bothFlag(shortName, longName, help2, parser2) { return { kind: "Both", shortName, longName, help: help2, parser: parser2 }; } // src/baner.ts function parser(...flags) { const collectedFlags = {}; for (const flag of flags) { switch (flag.kind) { case "Short": { collectedFlags[flag.name] = flag; break; } case "Long": { collectedFlags[flag.name] = flag; break; } case "Both": { collectedFlags[flag.longName] = flag; break; } } } return { flags: collectedFlags }; } function isFlag(string2) { const isNumber = isNaN(parseFloat(string2)); if (isNumber) { return string2.startsWith("-"); } return false; } function runString(parseable) { if (parseable.length === 0 || isFlag(parseable[0])) return Err("Not enough arguments. Expected a string."); return Ok(parseable[0]); } function runNumber(parseable) { if (parseable.length === 0 || isFlag(parseable[0])) return Err("Not enough arguments. Expected a number."); const parsed = parseFloat(parseable[0]); if (isNaN(parsed)) return Err("Not a number argument"); return Ok(parsed); } function runBoolean(parseable) { if (parseable.length === 0 || isFlag(parseable[0])) { return Ok(true); } const parsed = parseable[0] === "true" || parseable[0] === "false"; if (!parsed) return Err("Not a boolean argument"); return Ok(parseable[0] === "true"); } function runList(flagArguments, parseable) { const results = []; for (var i = 0; i < flagArguments.length; i++) { const argument = flagArguments[i]; const res = runArgument(argument, parseable.slice(i)); if (res.kind === "Err") { if (i >= parseable.length || isFlag(parseable[i])) { return Err(`${res.error} at index ${i}`); } return res; } else { results.push(res.value); } } return Ok(results); } function runOneOf(items, parseable) { if (parseable.length === 0 || isFlag(parseable[0])) return Err( `Not enough arguments. Expected one of: ${items.join(" | ")}.` ); for (var i = 0; i < items.length; i++) { const item = items[i]; if (item === parseable[0]) return Ok(item); } return Err(`Didn't match any of: ${items.join(" | ")}`); } function runVariableList(flagArgument, parseable) { const results = []; for (var i = 0; i < parseable.length; i++) { if (isFlag(parseable[i])) break; const res = runArgument(flagArgument, parseable.slice(i)); if (res.kind === "Err") return res; results.push(res.value); } return Ok(results); } function runArgument(argument, parseable) { switch (argument.kind) { case "StringArgument": { return runString(parseable); } case "NumberArgument": { return runNumber(parseable); } case "BooleanArgument": { return runBoolean(parseable); } case "ListArgument": { return runList(argument.items, parseable); } case "VariableListArgument": { return runVariableList( argument.item, parseable ); } case "OneOfArgument": { return runOneOf(argument.items, parseable); } } } function runShortFlag(flag, parseable) { const flagName = flag.name; const innerParser = flag.parser; if (parseable.length === 0) { return { isPresent: false, arguments: Err(`Short flag -${flagName} not found`), flag }; } for (var i = 0; i < parseable.length; i++) { const value = parseable[i]; if (value === `-${flagName}`) { let res = runArgument(innerParser, parseable.slice(i + 1)); if (res.kind === "Err") { res = Err(`Error parsing -${flagName} due to: ${res.error}`); } return { isPresent: true, arguments: res, flag }; } } return { isPresent: false, arguments: Err(`Short flag -${flagName} not found`), flag }; } function runLongFlag(flag, parseable) { const flagName = flag.name; const innerParser = flag.parser; if (parseable.length === 0) { return { isPresent: false, arguments: Err(`Long flag --${flagName} not found`), flag }; } for (var i = 0; i < parseable.length; i++) { const value = parseable[i]; if (value === `--${flagName}`) { let res = runArgument(innerParser, parseable.slice(i + 1)); if (res.kind === "Err") { res = Err(`Error parsing --${flagName} due to: ${res.error}`); } return { isPresent: true, arguments: res, flag }; } } return { isPresent: false, arguments: Err(`Long flag --${flagName} not found`), flag }; } function runBothFlag(flag, parseable) { const shortFlagName = flag.shortName; const longFlagName = flag.longName; const innerParser = flag.parser; const combinedFlagName = `-${shortFlagName}/--${longFlagName}`; if (parseable.length === 0) { return { isPresent: false, arguments: Err(`Mixed flag ${combinedFlagName} not found`), flag }; } for (var i = 0; i < parseable.length; i++) { const value = parseable[i]; if (value === `-${shortFlagName}` || value === `--${longFlagName}`) { let res = runArgument(innerParser, parseable.slice(i + 1)); if (res.kind === "Err") { res = Err( `Error parsing ${combinedFlagName} due to: ${res.error}` ); } return { isPresent: true, arguments: res, flag }; } } return { isPresent: false, arguments: Err(`Mixed flag ${combinedFlagName} not found`), flag }; } function runParser(programParser, parseable) { const emptyRecord = { args: parseable, flags: {} }; for (const flag of Object.values(programParser.flags)) { let res; switch (flag.kind) { case "Short": { res = runShortFlag(flag, parseable); break; } case "Long": { res = runLongFlag(flag, parseable); break; } case "Both": { res = runBothFlag(flag, parseable); } } let name; switch (flag.kind) { case "Short": { name = flag.name; break; } case "Long": { name = flag.name; break; } case "Both": { name = flag.longName; break; } } emptyRecord.flags[name] = { isPresent: res.isPresent, arguments: res.arguments, flag }; } return emptyRecord; } function helpFlagArgumentParser(parser2) { switch (parser2.kind) { case "BooleanArgument": return "boolean"; case "NumberArgument": return "number"; case "StringArgument": return "string"; case "ListArgument": return "[" + parser2.items.map(helpFlagArgumentParser).join(" ") + "]"; case "VariableListArgument": return "[" + helpFlagArgumentParser(parser2.item) + "...]"; case "OneOfArgument": return parser2.items.join(" | "); } } function help(flagParser) { const output = []; for (const flag of Object.values(flagParser.flags)) { switch (flag.kind) { case "Short": { output.push( ` -${flag.name} ${helpFlagArgumentParser( flag.parser )}: ${flag.help}` ); break; } case "Long": { output.push( ` --${flag.name} ${helpFlagArgumentParser( flag.parser )}: ${flag.help}` ); break; } case "Both": { output.push( ` -${flag.shortName}, --${flag.longName} ${helpFlagArgumentParser(flag.parser)}: ${flag.help}` ); break; } } } return output.join("\n"); } function allErrors(program) { const errors = []; for (const key of Object.keys(program.flags)) { const value = program.flags[key]; if (!value.isPresent) continue; const argument = value.arguments; if (argument.kind === "Err") { errors.push(argument.error); } } return errors; } function allMissing(program, ignore) { const errors = []; for (const key of Object.keys(program.flags)) { if (ignore.indexOf(key) > -1) continue; if (!program.flags[key].isPresent) errors.push(key); } return errors; } function allValues(program) { const values = {}; for (const key of Object.keys(program.flags)) { if (!program.flags[key].isPresent) continue; const argument = program.flags[key].arguments; if (argument.kind === "Ok") { values[key] = argument.value; } else { values[key] = void 0; } } return values; } function parse(flagParser, args) { const parseable = []; for (const arg of args) { if (arg.indexOf("=") > -1) { parseable.push(arg.split("=")[0]); arg.split("=")[1].split(",").forEach((splitArg) => { parseable.push(splitArg); }); } else { arg.split(",").forEach((splitArg) => { parseable.push(splitArg); }); } } const res = runParser(flagParser, parseable); return res; } export { Err, Ok, allErrors, allMissing, allValues, boolean, bothFlag, empty, help, list, longFlag, number, oneOf, parse, parser, shortFlag, string, variableList };