import { Either, type Mapper, type IEither, Optional } 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 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(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(v)) .get(); }; 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); }; const res = args .map(processArg) .reduce( (acc: IEither>, current: IEither) => acc.flatMap((accValue) => current.mapRight(([key, value]) => ({ ...accValue, [key]: value, })), ), Either.right(>{}), ) .mapRight((result) => result); return res; };