import z from "zod"; import { CommandExec } from "./cmdtypes.ts"; export interface CommandOptions { key: string[], subcommands?: Command[], exec?: CommandExec, zod?: z.ZodTuple, help?: string } export default class Command { subCmds: Command[]; key: string[]; exec: CommandExec | null; validate: z.ZodTuple | null; help: string | null; #argsLength: number; constructor(options: CommandOptions) { this.subCmds = options.subcommands || []; this.key = options.key; this.exec = options.exec || null; this.validate = options.zod || null; this.help = options.help || null; this.#argsLength = options.zod ? options.zod.def.items.length : 0; } getKey() { return this.key; } dispatch(...args: unknown[]): unknown | Promise { const root = args[0] as string | undefined; if (!root && this.exec) return this.exec(...args); else if (!root) return new Error('No execution target for this root'); const cmd = this.subCmds.find(cmd => cmd.getKey().includes(root)); if (cmd) { const newArgs = args.slice(1); if (cmd.#argsLength && newArgs.length !== cmd.#argsLength && cmd.help) return new Error(cmd.help); if (cmd.validate) { const res = cmd.validate.safeParse(newArgs); if (res.success) return cmd.dispatch(...res.data); else if (cmd.help) return new Error(cmd.help); else if (cmd.#argsLength) return new Error(`'${root}' validation error: expected ${cmd.#argsLength} args, got ${newArgs.length}`); else return new Error(`'${root}' validation error`); } } if (this.exec) return this.exec(...args); else return new Error(`'${root}': Subcommand not found (args: "${args.join(" ")}")`); } }