diff --git a/src/lib/options.test.ts b/src/lib/options.test.ts new file mode 100644 index 0000000..77c08cd --- /dev/null +++ b/src/lib/options.test.ts @@ -0,0 +1,28 @@ +import { parse } from "../parser"; +import { Script, TextPiece } from "../words"; +import { getOpt, Options } from "./options"; + +describe("getOpt", () => { + const expectOpts =

(command: string, opts: P) => { + const [ok, script] = parse(command); + expect(ok).toBeTruthy(); + return expect(getOpt((script as Script)[0] as TextPiece[], opts)); + }; + + it("parses empty arguments", () => expectOpts("cmd", {}).toEqual([{}])); + it("parses single positional arguments", () => + expectOpts("cmd apple", {}).toEqual([{}, "apple"])); + it("parses multiple positional arguments", () => + expectOpts("cmd apple banana", {}).toEqual([{}, "apple", "banana"])); + + it("enforces minimum # of arguments", () => + expectOpts("cmd", { $min: 1 }).toEqual([ + { error: "cmd: Not enough arguments" }, + ])); + it("enforces maximum # of arguments", () => + expectOpts("cmd apple banana", { $max: 1 }).toEqual([ + { error: "cmd: Too many arguments" }, + ])); + it("allows # of arguments in-spec", () => + expectOpts("cmd apple", { $min: 1, $max: 1 }).toEqual([{}, "apple"])); +}); diff --git a/src/lib/options.ts b/src/lib/options.ts index 3568f9d..7843fc3 100644 --- a/src/lib/options.ts +++ b/src/lib/options.ts @@ -1,11 +1,11 @@ -import { AsText, TextPiece } from '../words'; +import { AsText, TextPiece } from "../words"; -type SwitchPattern = Record & { - min?: number; - max?: number; +export type Options = Record & { + $min?: number; + $max?: number; }; -type SwitchOutput

= { - [K in keyof P]: K extends "min" | "max" +type ParsedOptions

= { + [K in keyof P]: K extends "$min" | "$max" ? never : P[K] extends 0 ? boolean @@ -14,20 +14,39 @@ type SwitchOutput

= { error?: string; }; -// TODO: tests and error handling -export function getOpt

( +export function getOpt

( argv: TextPiece[], - switches: P -): [SwitchOutput

, ...string[]] { - const [flags, textPieces] = getOptRaw(argv, switches); + options: P +): [ParsedOptions

, ...string[]] { + const [flags, textPieces] = getOptRaw(argv, options); return [flags, ...textPieces.map(AsText)]; } -export function getOptRaw

( +export function getOptRaw

( argv: TextPiece[], - switches: P -): [SwitchOutput

, TextPiece[]] { - const [, ...words] = argv; - // TODO: handle min/max - return [{} as SwitchOutput

, words]; + options: P +): [ParsedOptions

, TextPiece[]] { + const [cmd, ...words] = argv; + const result: ParsedOptions

= Object.fromEntries( + Object.entries(options) + .filter(([name]) => name != "$min" && name != "$max") + .map(([name]) => []) + ); + + // TODO: parse switches + + if (options.$min !== undefined && options.$min > words.length) { + return [ + { error: `${AsText(cmd)}: Not enough arguments` } as ParsedOptions

, + [], + ]; + } + if (options.$max !== undefined && options.$max < words.length) { + return [ + { error: `${AsText(cmd)}: Too many arguments` } as ParsedOptions

, + [], + ]; + } + + return [result, words]; }