aboutsummaryrefslogtreecommitdiff
path: root/src/args.ts
blob: d233c18add58fdf2b889aef510a09539ac919055 (plain) (blame)
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import * as E from "fp-ts/lib/Either";
import * as O from "fp-ts/lib/Option";
import * as R from "fp-ts/lib/ReadonlyArray";
import { pipe } from "fp-ts/lib/function";
import { parseTestType, type TestType } from "./canary";

export interface Args {
  readonly testsFile: string;
  readonly testName: O.Option<string>;
  readonly testType: O.Option<TestType>;
}

interface ArgsBuilder {
  readonly testsFile: O.Option<string>;
  readonly testName: O.Option<string>;
  readonly testType: O.Option<string>;
}

type ArgsBuilderField = (arg: string) => (builder: ArgsBuilder) => ArgsBuilder;

const createArgsBuilder = (): ArgsBuilder => ({
  testsFile: O.none,
  testName: O.none,
  testType: O.none,
});

const withTestsFile: ArgsBuilderField = (testsFile) => (builder) => ({
  ...builder,
  testsFile: O.some(testsFile),
});

const withTestName: ArgsBuilderField = (testName) => (builder) => ({
  ...builder,
  testName: O.some(testName),
});

const withTestType: ArgsBuilderField = (testType) => (builder) => ({
  ...builder,
  testType: O.some(testType),
});

const flagBuilders: Record<string, ArgsBuilderField> = {
  "--tests-file": withTestsFile,
  "--test-name": withTestName,
  "--test-type": withTestType,
};

const buildArgs = (builder: ArgsBuilder): E.Either<string, Args> => {
  if (O.isNone(builder.testsFile)) {
    return E.left("please specify a test file");
  }

  const testType = pipe(builder.testType, O.flatMap(parseTestType));
  if (O.isSome(builder.testType) && !O.isSome(testType)) {
    return E.left("bad test type " + builder.testType.value);
  }

  return E.right({
    testsFile: builder.testsFile.value,
    testName: builder.testName,
    testType,
  });
};

const isArg = (arg: string) => arg.startsWith("--") && arg in flagBuilders;

export const parseArgs = (argv: string[]): E.Either<string, Args> => {
  if (argv.length < 2) return E.left("bad argv");

  const useful = argv.slice(2);
  const argApplicators = pipe(
    useful,
    R.mapWithIndex((i, arg) => (isArg(arg) ? O.some({ arg, i }) : O.none)),
    R.compact,
    R.map(({ arg, i }) =>
      pipe(
        O.fromNullable(flagBuilders[arg]),
        O.bindTo("argApplicator"),
        O.bind("val", () =>
          pipe(
            O.fromNullable(useful.at(i + 1)),
            O.flatMap((argValue) =>
              isArg(argValue) ? O.none : O.some(argValue),
            ),
          ),
        ),
        O.map(({ val, argApplicator }) => argApplicator(val)),
      ),
    ),
  );

  return pipe(
    argApplicators,
    R.compact,
    R.reduce(createArgsBuilder(), (builder, applyArgTo) => applyArgTo(builder)),
    buildArgs,
  );
};