import { Either, type Mapper, type IEither } from '@emprespresso/pengueno'; export const isArgKey = (k: string): k is K => k.startsWith('--'); interface ArgHandler { absent?: V; unspecified?: V; present: Mapper; } export const getArg = ( arg: K, argv: Array, whenValue: ArgHandler, ): IEither => { const value = argv .filter((_argv) => isArgKey(_argv) && _argv.split('=')[0] === arg) .map((_argv, i) => { const next = _argv.includes('=') ? _argv.split('=')[1] : argv.at(i + 1); if (next) { if (isArgKey(next)) return whenValue.unspecified; return whenValue.present(next); } return whenValue.unspecified; }) .find((x) => x) ?? whenValue.absent; if (value === undefined) { return Either.left(new Error('no value specified for ' + arg)); } return Either.right(value); }; type MappedArgs< Args extends ReadonlyArray, Handlers extends Partial>>, > = { [K in Args[number]]: K extends keyof Handlers ? (Handlers[K] extends ArgHandler ? T : string) : string; }; export const argv = < const Args extends ReadonlyArray, const Handlers extends Partial>>, >( args: Args, handlers?: Handlers, argv = process.argv.slice(2), ): IEither> => { type Result = MappedArgs; const defaultHandler: ArgHandler = { present: (value: string) => value }; const processArg = (arg: Args[number]): IEither => { const handler = handlers?.[arg] ?? defaultHandler; return getArg(arg, argv, handler).mapRight((value) => [arg, value] as const); }; return args .map(processArg) .reduce( (acc: IEither>, current: IEither) => acc.flatMap((accValue) => current.mapRight(([key, value]) => ({ ...accValue, [key]: value, })), ), Either.right(>{}), ) .mapRight((result) => result); };