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; readonly testType: O.Option; } interface ArgsBuilder { readonly testsFile: O.Option; readonly testName: O.Option; readonly testType: O.Option; } 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 = { "--tests-file": withTestsFile, "--test-name": withTestName, "--test-type": withTestType, }; const buildArgs = (builder: ArgsBuilder): E.Either => { 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 => { 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, ); };