Files
galvanic-corrosion-rewrite/src/server/commands/command.ts
2025-07-25 19:00:06 -04:00

58 lines
1.9 KiB
TypeScript

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<unknown> {
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(" ")}")`);
}
}