summaryrefslogtreecommitdiff
path: root/u/process/argv.ts
blob: 45f8ff01cf42fd81619f7f279d2d5fc02fdc50e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import { Either, type Mapper, type IEither } from "@emprespresso/pengueno";

export const isArgKey = <K extends string>(k: string): k is K =>
  k.startsWith("--");

interface ArgHandler<V> {
    absent?: V;
    unspecified?: V;
    present: Mapper<string, V>;
}

export const getArg = <K extends string, V>(
  arg: K,
  argv: Array<string>,
  whenValue: ArgHandler<V>,
): IEither<Error, V> => {
    const value = argv.filter((_argv) => isArgKey(_argv) && _argv.split("=")[0] === arg).map((_argv, i) => {
        const next = _argv.includes("=") ? _argv.split("=")[1] : argv.at(i + 1);
        if (next) {
            if (isArgKey(next)) return whenValue.unspecified;
            return whenValue.present(next);
        }
        return whenValue.unspecified;
    }).find(x => x) ?? whenValue.absent;
    if (value === undefined) {
        return Either.left(new Error("no value specified for " + arg));
    }
    return Either.right(value);
};

type MappedArgs<
  Args extends ReadonlyArray<string>,
  Handlers extends Partial<Record<Args[number], ArgHandler<unknown>>>
> = {
  [K in Args[number]]: K extends keyof Handlers
    ? Handlers[K] extends ArgHandler<infer T>
      ? T
      : string
    : string;
};

export const argv = <
  const Args extends ReadonlyArray<string>,
  const Handlers extends Partial<Record<Args[number], ArgHandler<unknown>>>
>(
    args: Args,
    handlers?: Handlers,
    argv = Deno.args,
): IEither<Error, MappedArgs<Args, Handlers>> => {
    type Result = MappedArgs<Args, Handlers>;

    const defaultHandler: ArgHandler<string> = { present: (value: string) => value };

    const processArg = (arg: Args[number]): IEither<Error, [Args[number], unknown]> => {
        const handler = handlers?.[arg] ?? defaultHandler;
        return getArg(arg, argv, handler).mapRight(value => [arg, value] as const);
    };

    const argResults = args.map(processArg);

    return argResults.reduce(
        (acc: IEither<Error, Partial<Result>>, current: IEither<Error, readonly [Args[number], unknown]>) => {
            return acc.flatMap(accValue =>
                current.mapRight(([key, value]) => ({
                    ...accValue,
                    [key]: value
                }))
            );
        },
        Either.right({} as Partial<Result>)
    ).mapRight(result => result as Result);
};