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,
);
};
|