From 97561a4b7f61b375bfe52b912aabbaa0d9a785eb Mon Sep 17 00:00:00 2001 From: "Navy.gif" Date: Fri, 12 May 2023 17:24:59 +0300 Subject: [PATCH] Refactor parser to allow parsing of just arguments --- src/Parser.ts | 60 +++++++++++++++++++++++++++++--------------- tests/Parser.test.js | 22 ++++++++++++++++ 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/Parser.ts b/src/Parser.ts index e961e51..d1c0067 100644 --- a/src/Parser.ts +++ b/src/Parser.ts @@ -26,6 +26,12 @@ type ParserOptions = { resolver?: IResolver } +type ParseOptions = { + prefix?: string, + commandFilter?: (cmd: Command) => boolean, + guild?: unknown +} + class ParseError extends Error {} const flagReg = /(?:^| )(?(?:--[a-z0-9]{3,})|(?:-[a-z]{1,2}))(?:$| )/iu; @@ -75,8 +81,12 @@ class Parser extends EventEmitter { */ // eslint-disable-next-line @typescript-eslint/ban-types async parseMessage ( - message: string, prefix = this.prefix, - guild?: unknown, commandFilter?: (command: Command) => boolean + message: string, + { + prefix = this.prefix, + guild, + commandFilter + }: ParseOptions = {} ): Promise { if (!message.startsWith(prefix) || !message.length) @@ -134,11 +144,20 @@ class Parser extends EventEmitter { } const activeCommand = subcommand || command; - const flags = activeCommand.options.filter((opt) => opt.flag); params = Util.parseQuotes(params.join(' ')).map(([ str ]: (string | boolean)[]): string => str.toString()); this.debug(`Given params: "${params.join('", "')}"`); + + parseResult.args = await this.parseOptions(params, activeCommand.options, guild); + + return parseResult; + + } + + async parseOptions (params: string[], options: CommandOption[], guild?: unknown) { let currentFlag = null; + const flags = options.filter((opt) => opt.flag); + const args: ArgsResult = {}; // Parse flags for (let index = 0; index < params.length;) { @@ -202,12 +221,13 @@ class Parser extends EventEmitter { } this.debug(`Params after parsing "${params.join('", "')}"`); - const options = activeCommand.options.filter((opt) => !opt.flag && (opt.type !== OptionType.STRING || opt.choices.length)); - const stringOpts = activeCommand.options.filter((opt) => !opt.flag && !opt.choices.length && opt.type === OptionType.STRING); + // Non-string options + const nStrOptions = options.filter((opt) => !opt.flag && (opt.type !== OptionType.STRING || opt.choices.length)); + const stringOpts = options.filter((opt) => !opt.flag && !opt.choices.length && opt.type === OptionType.STRING); this.debug(`Parsing non-flag options`); // Parse out non-flag options - for (const option of options) { // String options are parsed separately at the end + for (const option of nStrOptions) { // String options are parsed separately at the end if (!params.some((param) => param !== null)) { this.debug(`No potential values left in params`); break; @@ -279,26 +299,26 @@ class Parser extends EventEmitter { } // This part is obsolete now, I think, the string option checks the choice value - for (const arg of Object.values(args)) { - if (!arg.choices.length) - continue; - if (!arg.choices.some((choice) => { - if (typeof arg.value === 'string' && typeof choice === 'string') - return arg.value.toLowerCase() === choice.toLowerCase(); - return arg.value === choice; - })) - throw new ParseError(`Invalid choice: ${arg.name} value must be one of ${arg.choices.join(', ')}`); // return { error: true, index: 'O_COMMANDHANDLER_INVALID_CHOICE', params: { option: arg.name, value: arg.value, choices: arg.choices.map((c) => c).join('`, `') } }; - } + // for (const arg of Object.values(args)) { + // if (!arg.choices.length) + // continue; + // if (!arg.choices.some((choice) => { + // if (typeof arg.value === 'string' && typeof choice === 'string') + // return arg.value.toLowerCase() === choice.toLowerCase(); + // return arg.value === choice; + // })) + // throw new ParseError(`Invalid choice: ${arg.name} value must be one of ${arg.choices.join(', ')}`); // return { error: true, index: 'O_COMMANDHANDLER_INVALID_CHOICE', params: { option: arg.name, value: arg.value, choices: arg.choices.map((c) => c).join('`, `') } }; + // } this.debug(`Making sure required options were given.`); - for (const req of activeCommand.options.filter((opt) => opt.required)) + for (const req of options.filter((opt) => opt.required)) if (!args[req.name]) throw new ParseError(`${req.name} is a required option`); if (strings.length) - throw new ParseError(`Unrecognised option(s): "${strings.join('", "')}"`);// return { error: true, index: 'O_COMMANDHANDLER_UNRECOGNISED_OPTIONS', params: { opts: strings.join('`, `') } }; - - return parseResult; + throw new ParseError(`Unrecognised option(s): "${strings.join('", "')}"`); + + return args; } diff --git a/tests/Parser.test.js b/tests/Parser.test.js index 3ae288c..7c9c6e7 100644 --- a/tests/Parser.test.js +++ b/tests/Parser.test.js @@ -95,4 +95,26 @@ test('Command Parser', async () => { await expect(parser.parseMessage('create code')).resolves.toHaveProperty('command.name', 'create'); await expect(parser.parseMessage('create code 1')).resolves.toHaveProperty('args.amount.value', 1); +}); + +test('Choice option', async () => { + + const command = new Command({ + name: 'create', + options: [{ + name: 'registration-code', + aliases: ['code'], + type: OptionType.SUB_COMMAND, + options: [{ + name: 'amount', + type: OptionType.STRING, + choices: ['one', 'two'] + }] + }] + }); + const parser = new Parser({ commands: [command], prefix: '' }); + + await expect(parser.parseMessage('create code 1')).rejects.toThrow(); + await expect(parser.parseMessage('create code one')).resolves.toHaveProperty('args.amount.value', 'one'); + }); \ No newline at end of file