This repository has been archived on 2026-02-01. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
diarywhisper/utils/commands/helpers/commandParser.ts
2025-01-19 21:06:15 +00:00

150 lines
3.7 KiB
TypeScript

import { CommandDefinition } from '@utils/types';
export class CommandParser {
private commands: Map<string, CommandDefinition>;
constructor() {
this.commands = new Map();
}
registerCommand(definition: CommandDefinition) {
this.commands.set(definition.name.toLowerCase(), definition);
}
parse(input: string): {
command: string;
id?: string;
flags: Record<string, string | number | boolean | Date>;
} {
const parts = input.match(/(?:[^\s"]+|"[^"]*")+/g);
if (!parts || parts.length === 0) {
throw new Error('Invalid command format');
}
const command = parts[0].toLowerCase();
const definition = this.commands.get(command);
if (!definition) {
throw new Error(`Unknown command: ${command}`);
}
let currentIndex = 1;
const flags: Record<string, string | number | boolean | Date> = {};
if (definition.hasId) {
if (parts.length < 2) {
throw new Error(`Command ${command} requires an ID`);
}
const id = parts[1];
currentIndex = 2;
flags.id = id;
}
while (currentIndex < parts.length) {
const flag = parts[currentIndex];
if (!flag.startsWith('-')) {
throw new Error(`Invalid flag format at: ${flag}`);
}
const flagName = flag.slice(1);
const flagDef = definition.flags.find(
f => f.name === flagName || f.alias === flagName
);
if (!flagDef) {
throw new Error(`Unknown flag: ${flagName}`);
}
currentIndex++;
if (currentIndex >= parts.length) {
throw new Error(`Missing value for flag: ${flagName}`);
}
const value = parts[currentIndex].replace(/^"(.*)"$/, '$1');
switch (flagDef.type) {
case 'number': {
const num = Number(value);
if (isNaN(num)) {
throw new Error(`Invalid number for flag ${flagName}: ${value}`);
}
flags[flagDef.name] = num;
break;
}
case 'date': {
const date = new Date(value);
if (isNaN(date.getTime())) {
throw new Error(`Invalid date for flag ${flagName}: ${value}`);
}
flags[flagDef.name] = date;
break;
}
case 'boolean': {
flags[flagDef.name] = value.toLowerCase() === 'true';
break;
}
default: {
flags[flagDef.name] = value;
}
}
currentIndex++;
}
for (const flagDef of definition.flags) {
if (flagDef.required && !(flagDef.name in flags)) {
throw new Error(`Missing required flag: ${flagDef.name}`);
}
}
return {
command,
...(flags.id ? { id: flags.id as string } : {}),
flags: Object.fromEntries(
Object.entries(flags).filter(([key]) => key !== 'id')
)
};
}
}
export const diaryCommands: CommandDefinition[] = [
{
name: 'add',
flags: [
{ name: 'desc', type: 'string', required: true },
{ name: 'cost', type: 'number', required: true },
{ name: 'cat', type: 'string', required: false }
]
},
{
name: 'update',
hasId: true,
flags: [
{ name: 'desc', type: 'string', required: false },
{ name: 'cost', type: 'number', required: false },
{ name: 'cat', type: 'string', required: false }
]
},
{
name: 'delete',
hasId: true,
flags: []
},
{
name: 'report',
flags: [
{ name: 'from', type: 'date', required: true },
{ name: 'to', type: 'date', required: true },
{ name: 'export', type: 'boolean', required: false }
]
},
{
name: 'daylog',
flags: [
{ name: 'stars', type: 'number', required: true },
{ name: 'text', type: 'string', required: true },
{ name: 'date', type: 'date', required: false }
]
}
];