summaryrefslogtreecommitdiff
path: root/lib/process/argv.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/process/argv.ts')
-rw-r--r--lib/process/argv.ts79
1 files changed, 79 insertions, 0 deletions
diff --git a/lib/process/argv.ts b/lib/process/argv.ts
new file mode 100644
index 0000000..396fa96
--- /dev/null
+++ b/lib/process/argv.ts
@@ -0,0 +1,79 @@
+import { Either, type Mapper, type IEither, Optional } 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 argIndex = Optional.from(argv.findIndex((_argv) => isArgKey(_argv) && _argv.split('=')[0] === arg)).filter(
+ (index) => index >= 0 && index < argv.length,
+ );
+ if (!argIndex.present()) {
+ return Optional.from(whenValue.absent)
+ .map((v) => Either.right<Error, V>(v))
+ .orSome(() =>
+ Either.left(
+ new Error(`arg ${arg} is not present in arguments list and does not have an 'absent' value`),
+ ),
+ )
+ .get();
+ }
+
+ return argIndex
+ .flatMap((idx) =>
+ Optional.from(argv.at(idx)).map((_argv) => (_argv.includes('=') ? _argv.split('=')[1] : argv.at(idx + 1))),
+ )
+ .filter((next) => !isArgKey(next))
+ .map((next) => whenValue.present(next))
+ .orSome(() => whenValue.unspecified)
+ .map((v) => Either.right<Error, V>(<V>v))
+ .get();
+};
+
+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 = process.argv.slice(2),
+): 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 res = args
+ .map(processArg)
+ .reduce(
+ (acc: IEither<Error, Partial<Result>>, current: IEither<Error, [Args[number], unknown]>) =>
+ acc.flatMap((accValue) =>
+ current.mapRight(([key, value]) => ({
+ ...accValue,
+ [key]: value,
+ })),
+ ),
+ Either.right(<Partial<Result>>{}),
+ )
+ .mapRight((result) => <Result>result);
+ return res;
+};