summaryrefslogtreecommitdiff
path: root/u/process/argv.ts
blob: 657c9a7a218d9bfe4ab524fee78f582d7da3a5b3 (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
import { Either, type IEither } from "@emprespresso/pengueno";

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

export const getArg = <K extends string, V extends string>(
  arg: K,
  args: Array<K>,
): IEither<Error, V> => {
  const result = args.findIndex((_arg) => _arg.startsWith(arg));
  if (result < 0) return Either.left(new Error("no argument found for " + arg));
  const [resultArg, next]: Array<string | undefined> = [
    args[result],
    args.at(result + 1),
  ];
  if (resultArg && resultArg.includes("=")) {
    return Either.right(resultArg.split("=")[1] as V);
  }
  if (typeof next === "undefined")
    return Either.left(new Error("no value specified for " + arg));
  if (isArgKey(next))
    return Either.left(
      new Error("next value for arg " + arg + " is another arg " + next),
    );
  return Either.right(next as V);
};

type ObjectFromList<T extends ReadonlyArray<string>, V = string> = {
  [K in T extends ReadonlyArray<infer U> ? U : never]: V;
};
export const argv = <K extends string, V extends string>(
  args: ReadonlyArray<K>,
  defaultArgs?: Partial<Record<K, V>>,
  argv = Deno.args,
) => {
  return args
    .map((arg) => [arg, getArg(arg, argv)] as [K, IEither<Error, V>])
    .map(([arg, specified]): [K, IEither<Error, V>] => [
      arg,
      specified.fold((e, val) => {
        const hasDefaultVal = e && defaultArgs && arg in defaultArgs;
        if (hasDefaultVal) {
          return Either.right<Error, V>(defaultArgs[arg]!);
        } else if (!val || e) {
          return Either.left<Error, V>(e ?? new Error("unknown"));
        }
        return Either.right<Error, V>(val);
      }),
    ])
    .reduce(
      (
        acc: IEither<Error, ObjectFromList<typeof args, V>>,
        x: [K, IEither<Error, V>],
      ): IEither<Error, ObjectFromList<typeof args, V>> => {
        const [arg, eitherVal] = x;
        return acc.flatMap((args) => {
          return eitherVal.mapRight((envValue) => ({
            ...args,
            [arg]: envValue,
          }));
        });
      },
      Either.right({} as ObjectFromList<typeof args, V>),
    );
};