Refactor parser to allow parsing of just arguments
This commit is contained in:
parent
aa7fe8643c
commit
97561a4b7f
@ -26,6 +26,12 @@ type ParserOptions = {
|
||||
resolver?: IResolver<unknown, unknown, unknown, unknown, unknown>
|
||||
}
|
||||
|
||||
type ParseOptions = {
|
||||
prefix?: string,
|
||||
commandFilter?: (cmd: Command) => boolean,
|
||||
guild?: unknown
|
||||
}
|
||||
|
||||
class ParseError extends Error {}
|
||||
|
||||
const flagReg = /(?:^| )(?<flag>(?:--[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<ParseResult | null> {
|
||||
|
||||
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('`, `') } };
|
||||
throw new ParseError(`Unrecognised option(s): "${strings.join('", "')}"`);
|
||||
|
||||
return parseResult;
|
||||
return args;
|
||||
|
||||
}
|
||||
|
||||
|
@ -96,3 +96,25 @@ test('Command Parser', async () => {
|
||||
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');
|
||||
|
||||
});
|
Loading…
Reference in New Issue
Block a user