* command execution rework and sync service scopes for typeconverters * replace ValueTask with Task * fix implementation bugs Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>pull/2520/head
@@ -23,7 +23,7 @@ namespace Discord.Interactions | |||
public string CommandName { get; } | |||
/// <inheritdoc/> | |||
public override IReadOnlyCollection<CommandParameterInfo> Parameters { get; } | |||
public override IReadOnlyList<CommandParameterInfo> Parameters { get; } | |||
/// <inheritdoc/> | |||
public override bool SupportsWildCards => false; | |||
@@ -41,9 +41,12 @@ namespace Discord.Interactions | |||
if (context.Interaction is not IAutocompleteInteraction) | |||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Autocomplete Interaction"); | |||
return await RunAsync(context, Array.Empty<object>(), services).ConfigureAwait(false); | |||
return await base.ExecuteAsync(context, services).ConfigureAwait(false); | |||
} | |||
protected override Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services) | |||
=> Task.FromResult(ParseResult.FromSuccess(Array.Empty<object>()) as IResult); | |||
/// <inheritdoc/> | |||
protected override Task InvokeModuleEvent(IInteractionContext context, IResult result) => | |||
CommandService._autocompleteCommandExecutedEvent.InvokeAsync(this, context, result); | |||
@@ -64,7 +64,7 @@ namespace Discord.Interactions | |||
public IReadOnlyCollection<PreconditionAttribute> Preconditions { get; } | |||
/// <inheritdoc cref="ICommandInfo.Parameters"/> | |||
public abstract IReadOnlyCollection<TParameter> Parameters { get; } | |||
public abstract IReadOnlyList<TParameter> Parameters { get; } | |||
internal CommandInfo(Builders.ICommandBuilder builder, ModuleInfo module, InteractionService commandService) | |||
{ | |||
@@ -85,71 +85,16 @@ namespace Discord.Interactions | |||
} | |||
/// <inheritdoc/> | |||
public abstract Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services); | |||
protected abstract Task InvokeModuleEvent(IInteractionContext context, IResult result); | |||
protected abstract string GetLogString(IInteractionContext context); | |||
/// <inheritdoc/> | |||
public async Task<PreconditionResult> CheckPreconditionsAsync(IInteractionContext context, IServiceProvider services) | |||
{ | |||
async Task<PreconditionResult> CheckGroups(ILookup<string, PreconditionAttribute> preconditions, string type) | |||
{ | |||
foreach (IGrouping<string, PreconditionAttribute> preconditionGroup in preconditions) | |||
{ | |||
if (preconditionGroup.Key == null) | |||
{ | |||
foreach (PreconditionAttribute precondition in preconditionGroup) | |||
{ | |||
var result = await precondition.CheckRequirementsAsync(context, this, services).ConfigureAwait(false); | |||
if (!result.IsSuccess) | |||
return result; | |||
} | |||
} | |||
else | |||
{ | |||
var results = new List<PreconditionResult>(); | |||
foreach (PreconditionAttribute precondition in preconditionGroup) | |||
results.Add(await precondition.CheckRequirementsAsync(context, this, services).ConfigureAwait(false)); | |||
if (!results.Any(p => p.IsSuccess)) | |||
return PreconditionGroupResult.FromError($"{type} precondition group {preconditionGroup.Key} failed.", results); | |||
} | |||
} | |||
return PreconditionGroupResult.FromSuccess(); | |||
} | |||
var moduleResult = await CheckGroups(Module.GroupedPreconditions, "Module").ConfigureAwait(false); | |||
if (!moduleResult.IsSuccess) | |||
return moduleResult; | |||
var commandResult = await CheckGroups(_groupedPreconditions, "Command").ConfigureAwait(false); | |||
return !commandResult.IsSuccess ? commandResult : PreconditionResult.FromSuccess(); | |||
} | |||
protected async Task<IResult> RunAsync(IInteractionContext context, object[] args, IServiceProvider services) | |||
public virtual async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services) | |||
{ | |||
switch (RunMode) | |||
{ | |||
case RunMode.Sync: | |||
{ | |||
if (CommandService._autoServiceScopes) | |||
{ | |||
using var scope = services?.CreateScope(); | |||
return await ExecuteInternalAsync(context, args, scope?.ServiceProvider ?? EmptyServiceProvider.Instance).ConfigureAwait(false); | |||
} | |||
return await ExecuteInternalAsync(context, args, services).ConfigureAwait(false); | |||
} | |||
return await ExecuteInternalAsync(context, services).ConfigureAwait(false); | |||
case RunMode.Async: | |||
_ = Task.Run(async () => | |||
{ | |||
if (CommandService._autoServiceScopes) | |||
{ | |||
using var scope = services?.CreateScope(); | |||
await ExecuteInternalAsync(context, args, scope?.ServiceProvider ?? EmptyServiceProvider.Instance).ConfigureAwait(false); | |||
} | |||
else | |||
await ExecuteInternalAsync(context, args, services).ConfigureAwait(false); | |||
await ExecuteInternalAsync(context, services).ConfigureAwait(false); | |||
}); | |||
break; | |||
default: | |||
@@ -159,16 +104,33 @@ namespace Discord.Interactions | |||
return ExecuteResult.FromSuccess(); | |||
} | |||
private async Task<IResult> ExecuteInternalAsync(IInteractionContext context, object[] args, IServiceProvider services) | |||
protected abstract Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services); | |||
private async Task<IResult> ExecuteInternalAsync(IInteractionContext context, IServiceProvider services) | |||
{ | |||
await CommandService._cmdLogger.DebugAsync($"Executing {GetLogString(context)}").ConfigureAwait(false); | |||
using var scope = services?.CreateScope(); | |||
if (CommandService._autoServiceScopes) | |||
services = scope?.ServiceProvider ?? EmptyServiceProvider.Instance; | |||
try | |||
{ | |||
var preconditionResult = await CheckPreconditionsAsync(context, services).ConfigureAwait(false); | |||
if (!preconditionResult.IsSuccess) | |||
return await InvokeEventAndReturn(context, preconditionResult).ConfigureAwait(false); | |||
var argsResult = await ParseArgumentsAsync(context, services).ConfigureAwait(false); | |||
if (!argsResult.IsSuccess) | |||
return await InvokeEventAndReturn(context, argsResult).ConfigureAwait(false); | |||
if(argsResult is not ParseResult parseResult) | |||
return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Complex command parsing failed for an unknown reason."); | |||
var args = parseResult.Args; | |||
var index = 0; | |||
foreach (var parameter in Parameters) | |||
{ | |||
@@ -221,7 +183,47 @@ namespace Discord.Interactions | |||
} | |||
} | |||
protected async ValueTask<IResult> InvokeEventAndReturn(IInteractionContext context, IResult result) | |||
protected abstract Task InvokeModuleEvent(IInteractionContext context, IResult result); | |||
protected abstract string GetLogString(IInteractionContext context); | |||
/// <inheritdoc/> | |||
public async Task<PreconditionResult> CheckPreconditionsAsync(IInteractionContext context, IServiceProvider services) | |||
{ | |||
async Task<PreconditionResult> CheckGroups(ILookup<string, PreconditionAttribute> preconditions, string type) | |||
{ | |||
foreach (IGrouping<string, PreconditionAttribute> preconditionGroup in preconditions) | |||
{ | |||
if (preconditionGroup.Key == null) | |||
{ | |||
foreach (PreconditionAttribute precondition in preconditionGroup) | |||
{ | |||
var result = await precondition.CheckRequirementsAsync(context, this, services).ConfigureAwait(false); | |||
if (!result.IsSuccess) | |||
return result; | |||
} | |||
} | |||
else | |||
{ | |||
var results = new List<PreconditionResult>(); | |||
foreach (PreconditionAttribute precondition in preconditionGroup) | |||
results.Add(await precondition.CheckRequirementsAsync(context, this, services).ConfigureAwait(false)); | |||
if (!results.Any(p => p.IsSuccess)) | |||
return PreconditionGroupResult.FromError($"{type} precondition group {preconditionGroup.Key} failed.", results); | |||
} | |||
} | |||
return PreconditionGroupResult.FromSuccess(); | |||
} | |||
var moduleResult = await CheckGroups(Module.GroupedPreconditions, "Module").ConfigureAwait(false); | |||
if (!moduleResult.IsSuccess) | |||
return moduleResult; | |||
var commandResult = await CheckGroups(_groupedPreconditions, "Command").ConfigureAwait(false); | |||
return !commandResult.IsSuccess ? commandResult : PreconditionResult.FromSuccess(); | |||
} | |||
protected async Task<T> InvokeEventAndReturn<T>(IInteractionContext context, T result) where T : IResult | |||
{ | |||
await InvokeModuleEvent(context, result).ConfigureAwait(false); | |||
return result; | |||
@@ -13,7 +13,7 @@ namespace Discord.Interactions | |||
public class ComponentCommandInfo : CommandInfo<ComponentCommandParameterInfo> | |||
{ | |||
/// <inheritdoc/> | |||
public override IReadOnlyCollection<ComponentCommandParameterInfo> Parameters { get; } | |||
public override IReadOnlyList<ComponentCommandParameterInfo> Parameters { get; } | |||
/// <inheritdoc/> | |||
public override bool SupportsWildCards => true; | |||
@@ -25,48 +25,32 @@ namespace Discord.Interactions | |||
/// <inheritdoc/> | |||
public override async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services) | |||
=> await ExecuteAsync(context, services, null).ConfigureAwait(false); | |||
/// <summary> | |||
/// Execute this command using dependency injection. | |||
/// </summary> | |||
/// <param name="context">Context that will be injected to the <see cref="InteractionModuleBase{T}"/>.</param> | |||
/// <param name="services">Services that will be used while initializing the <see cref="InteractionModuleBase{T}"/>.</param> | |||
/// <param name="additionalArgs">Provide additional string parameters to the method along with the auto generated parameters.</param> | |||
/// <returns> | |||
/// A task representing the asynchronous command execution process. | |||
/// </returns> | |||
public async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services, params string[] additionalArgs) | |||
{ | |||
if (context.Interaction is not IComponentInteraction componentInteraction) | |||
if (context.Interaction is not IComponentInteraction) | |||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Message Component Interaction"); | |||
return await ExecuteAsync(context, Parameters, additionalArgs, componentInteraction.Data, services); | |||
return await base.ExecuteAsync(context, services).ConfigureAwait(false); | |||
} | |||
/// <inheritdoc/> | |||
public async Task<IResult> ExecuteAsync(IInteractionContext context, IEnumerable<CommandParameterInfo> paramList, IEnumerable<string> wildcardCaptures, IComponentInteractionData data, | |||
IServiceProvider services) | |||
protected override async Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services) | |||
{ | |||
var paramCount = paramList.Count(); | |||
var captureCount = wildcardCaptures?.Count() ?? 0; | |||
if (context.Interaction is not IComponentInteraction messageComponent) | |||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Component Command Interaction"); | |||
var captures = (context as IRouteMatchContainer)?.SegmentMatches?.ToList(); | |||
var captureCount = captures?.Count() ?? 0; | |||
try | |||
{ | |||
var args = new object[paramCount]; | |||
var data = (context.Interaction as IComponentInteraction).Data; | |||
var args = new object[Parameters.Count]; | |||
for (var i = 0; i < paramCount; i++) | |||
for(var i = 0; i < Parameters.Count; i++) | |||
{ | |||
var parameter = Parameters.ElementAt(i); | |||
var parameter = Parameters[i]; | |||
var isCapture = i < captureCount; | |||
if (isCapture ^ parameter.IsRouteSegmentParameter) | |||
return await InvokeEventAndReturn(context, ExecuteResult.FromError(InteractionCommandError.BadArgs, "Argument type and parameter type didn't match (Wild Card capture/Component value)")).ConfigureAwait(false); | |||
var readResult = isCapture ? await parameter.TypeReader.ReadAsync(context, wildcardCaptures.ElementAt(i), services).ConfigureAwait(false) : | |||
var readResult = isCapture ? await parameter.TypeReader.ReadAsync(context, captures[i].Value, services).ConfigureAwait(false) : | |||
await parameter.TypeConverter.ReadAsync(context, data, services).ConfigureAwait(false); | |||
if (!readResult.IsSuccess) | |||
@@ -75,7 +59,7 @@ namespace Discord.Interactions | |||
args[i] = readResult.Value; | |||
} | |||
return await RunAsync(context, args, services).ConfigureAwait(false); | |||
return ParseResult.FromSuccess(args); | |||
} | |||
catch (Exception ex) | |||
{ | |||
@@ -24,7 +24,7 @@ namespace Discord.Interactions | |||
public GuildPermission? DefaultMemberPermissions { get; } | |||
/// <inheritdoc/> | |||
public override IReadOnlyCollection<CommandParameterInfo> Parameters { get; } | |||
public override IReadOnlyList<CommandParameterInfo> Parameters { get; } | |||
/// <inheritdoc/> | |||
public override bool SupportsWildCards => false; | |||
@@ -14,18 +14,23 @@ namespace Discord.Interactions | |||
/// <inheritdoc/> | |||
public override async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services) | |||
{ | |||
if (context.Interaction is not IMessageCommandInteraction messageCommand) | |||
if (context.Interaction is not IMessageCommandInteraction) | |||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Message Command Interation"); | |||
return await base.ExecuteAsync(context, services).ConfigureAwait(false); | |||
} | |||
protected override Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services) | |||
{ | |||
try | |||
{ | |||
object[] args = new object[1] { messageCommand.Data.Message }; | |||
object[] args = new object[1] { (context.Interaction as IMessageCommandInteraction).Data.Message }; | |||
return await RunAsync(context, args, services).ConfigureAwait(false); | |||
return Task.FromResult(ParseResult.FromSuccess(args) as IResult); | |||
} | |||
catch (Exception ex) | |||
{ | |||
return ExecuteResult.FromError(ex); | |||
return Task.FromResult(ParseResult.FromError(ex) as IResult); | |||
} | |||
} | |||
@@ -17,15 +17,20 @@ namespace Discord.Interactions | |||
if (context.Interaction is not IUserCommandInteraction userCommand) | |||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Message Command Interation"); | |||
return await base.ExecuteAsync(context, services).ConfigureAwait(false); | |||
} | |||
protected override Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services) | |||
{ | |||
try | |||
{ | |||
object[] args = new object[1] { userCommand.Data.User }; | |||
object[] args = new object[1] { (context.Interaction as IUserCommandInteraction).Data.User }; | |||
return await RunAsync(context, args, services).ConfigureAwait(false); | |||
return Task.FromResult(ParseResult.FromSuccess(args) as IResult); | |||
} | |||
catch (Exception ex) | |||
{ | |||
return ExecuteResult.FromError(ex); | |||
return Task.FromResult(ParseResult.FromError(ex) as IResult); | |||
} | |||
} | |||
@@ -1,7 +1,6 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Collections.Immutable; | |||
using System.Diagnostics.Tracing; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Discord.Interactions | |||
@@ -20,7 +19,7 @@ namespace Discord.Interactions | |||
public override bool SupportsWildCards => true; | |||
/// <inheritdoc/> | |||
public override IReadOnlyCollection<ModalCommandParameterInfo> Parameters { get; } | |||
public override IReadOnlyList<ModalCommandParameterInfo> Parameters { get; } | |||
internal ModalCommandInfo(Builders.ModalCommandBuilder builder, ModuleInfo module, InteractionService commandService) : base(builder, module, commandService) | |||
{ | |||
@@ -30,34 +29,29 @@ namespace Discord.Interactions | |||
/// <inheritdoc/> | |||
public override async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services) | |||
=> await ExecuteAsync(context, services, null).ConfigureAwait(false); | |||
/// <summary> | |||
/// Execute this command using dependency injection. | |||
/// </summary> | |||
/// <param name="context">Context that will be injected to the <see cref="InteractionModuleBase{T}"/>.</param> | |||
/// <param name="services">Services that will be used while initializing the <see cref="InteractionModuleBase{T}"/>.</param> | |||
/// <param name="additionalArgs">Provide additional string parameters to the method along with the auto generated parameters.</param> | |||
/// <returns> | |||
/// A task representing the asynchronous command execution process. | |||
/// </returns> | |||
public async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services, params string[] additionalArgs) | |||
{ | |||
if (context.Interaction is not IModalInteraction modalInteraction) | |||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Modal Interaction."); | |||
return await base.ExecuteAsync(context, services).ConfigureAwait(false); | |||
} | |||
protected override async Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services) | |||
{ | |||
var captures = (context as IRouteMatchContainer)?.SegmentMatches?.ToList(); | |||
var captureCount = captures?.Count() ?? 0; | |||
try | |||
{ | |||
var args = new object[Parameters.Count]; | |||
var captureCount = additionalArgs?.Length ?? 0; | |||
for(var i = 0; i < Parameters.Count; i++) | |||
for (var i = 0; i < Parameters.Count; i++) | |||
{ | |||
var parameter = Parameters.ElementAt(i); | |||
if(i < captureCount) | |||
if (i < captureCount) | |||
{ | |||
var readResult = await parameter.TypeReader.ReadAsync(context, additionalArgs[i], services).ConfigureAwait(false); | |||
var readResult = await parameter.TypeReader.ReadAsync(context, captures[i].Value, services).ConfigureAwait(false); | |||
if (!readResult.IsSuccess) | |||
return await InvokeEventAndReturn(context, readResult).ConfigureAwait(false); | |||
@@ -69,13 +63,14 @@ namespace Discord.Interactions | |||
if (!modalResult.IsSuccess) | |||
return await InvokeEventAndReturn(context, modalResult).ConfigureAwait(false); | |||
if (modalResult is not ParseResult parseResult) | |||
if (modalResult is not TypeConverterResult converterResult) | |||
return await InvokeEventAndReturn(context, ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command parameter parsing failed for an unknown reason.")); | |||
args[i] = parseResult.Value; | |||
args[i] = converterResult.Value; | |||
} | |||
} | |||
return await RunAsync(context, args, services); | |||
return ParseResult.FromSuccess(args); | |||
} | |||
catch (Exception ex) | |||
{ | |||
@@ -33,7 +33,7 @@ namespace Discord.Interactions | |||
public GuildPermission? DefaultMemberPermissions { get; } | |||
/// <inheritdoc/> | |||
public override IReadOnlyCollection<SlashCommandParameterInfo> Parameters { get; } | |||
public override IReadOnlyList<SlashCommandParameterInfo> Parameters { get; } | |||
/// <inheritdoc/> | |||
public override bool SupportsWildCards => false; | |||
@@ -41,9 +41,9 @@ namespace Discord.Interactions | |||
/// <summary> | |||
/// Gets the flattened collection of command parameters and complex parameter fields. | |||
/// </summary> | |||
public IReadOnlyCollection<SlashCommandParameterInfo> FlattenedParameters { get; } | |||
public IReadOnlyList<SlashCommandParameterInfo> FlattenedParameters { get; } | |||
internal SlashCommandInfo (Builders.SlashCommandBuilder builder, ModuleInfo module, InteractionService commandService) : base(builder, module, commandService) | |||
internal SlashCommandInfo(Builders.SlashCommandBuilder builder, ModuleInfo module, InteractionService commandService) : base(builder, module, commandService) | |||
{ | |||
Description = builder.Description; | |||
DefaultPermission = builder.DefaultPermission; | |||
@@ -60,49 +60,45 @@ namespace Discord.Interactions | |||
} | |||
/// <inheritdoc/> | |||
public override async Task<IResult> ExecuteAsync (IInteractionContext context, IServiceProvider services) | |||
public override async Task<IResult> ExecuteAsync(IInteractionContext context, IServiceProvider services) | |||
{ | |||
if(context.Interaction is not ISlashCommandInteraction slashCommand) | |||
if (context.Interaction is not ISlashCommandInteraction) | |||
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Slash Command Interaction"); | |||
var options = slashCommand.Data.Options; | |||
while (options != null && options.Any(x => x.Type == ApplicationCommandOptionType.SubCommand || x.Type == ApplicationCommandOptionType.SubCommandGroup)) | |||
options = options.ElementAt(0)?.Options; | |||
return await ExecuteAsync(context, Parameters, options?.ToList(), services); | |||
return await base.ExecuteAsync(context, services); | |||
} | |||
private async Task<IResult> ExecuteAsync (IInteractionContext context, IEnumerable<SlashCommandParameterInfo> paramList, | |||
List<IApplicationCommandInteractionDataOption> argList, IServiceProvider services) | |||
protected override async Task<IResult> ParseArgumentsAsync(IInteractionContext context, IServiceProvider services) | |||
{ | |||
try | |||
List<IApplicationCommandInteractionDataOption> GetOptions() | |||
{ | |||
var slashCommandParameterInfos = paramList.ToList(); | |||
var args = new object[slashCommandParameterInfos.Count]; | |||
for (var i = 0; i < slashCommandParameterInfos.Count; i++) | |||
{ | |||
var parameter = slashCommandParameterInfos[i]; | |||
var result = await ParseArgument(parameter, context, argList, services).ConfigureAwait(false); | |||
if (!result.IsSuccess) | |||
return await InvokeEventAndReturn(context, result).ConfigureAwait(false); | |||
var options = (context.Interaction as ISlashCommandInteraction).Data.Options; | |||
if (result is not ParseResult parseResult) | |||
return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command parameter parsing failed for an unknown reason."); | |||
while (options != null && options.Any(x => x.Type == ApplicationCommandOptionType.SubCommand || x.Type == ApplicationCommandOptionType.SubCommandGroup)) | |||
options = options.ElementAt(0)?.Options; | |||
args[i] = parseResult.Value; | |||
} | |||
return await RunAsync(context, args, services).ConfigureAwait(false); | |||
return options.ToList(); | |||
} | |||
catch(Exception ex) | |||
var options = GetOptions(); | |||
var args = new object[Parameters.Count]; | |||
for(var i = 0; i < Parameters.Count; i++) | |||
{ | |||
return await InvokeEventAndReturn(context, ExecuteResult.FromError(ex)).ConfigureAwait(false); | |||
var parameter = Parameters[i]; | |||
var result = await ParseArgumentAsync(parameter, context, options, services).ConfigureAwait(false); | |||
if (!result.IsSuccess) | |||
return await InvokeEventAndReturn(context, ParseResult.FromError(result)).ConfigureAwait(false); | |||
if (result is not TypeConverterResult converterResult) | |||
return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Complex command parsing failed for an unknown reason."); | |||
args[i] = converterResult.Value; | |||
} | |||
return ParseResult.FromSuccess(args); | |||
} | |||
private async Task<IResult> ParseArgument(SlashCommandParameterInfo parameterInfo, IInteractionContext context, List<IApplicationCommandInteractionDataOption> argList, | |||
private async ValueTask<IResult> ParseArgumentAsync(SlashCommandParameterInfo parameterInfo, IInteractionContext context, List<IApplicationCommandInteractionDataOption> argList, | |||
IServiceProvider services) | |||
{ | |||
if (parameterInfo.IsComplexParameter) | |||
@@ -111,32 +107,29 @@ namespace Discord.Interactions | |||
for (var i = 0; i < ctorArgs.Length; i++) | |||
{ | |||
var result = await ParseArgument(parameterInfo.ComplexParameterFields.ElementAt(i), context, argList, services).ConfigureAwait(false); | |||
var result = await ParseArgumentAsync(parameterInfo.ComplexParameterFields.ElementAt(i), context, argList, services).ConfigureAwait(false); | |||
if (!result.IsSuccess) | |||
return result; | |||
if (result is not ParseResult parseResult) | |||
if (result is not TypeConverterResult converterResult) | |||
return ExecuteResult.FromError(InteractionCommandError.BadArgs, "Complex command parsing failed for an unknown reason."); | |||
ctorArgs[i] = parseResult.Value; | |||
ctorArgs[i] = converterResult.Value; | |||
} | |||
return ParseResult.FromSuccess(parameterInfo._complexParameterInitializer(ctorArgs)); | |||
return TypeConverterResult.FromSuccess(parameterInfo._complexParameterInitializer(ctorArgs)); | |||
} | |||
var arg = argList?.Find(x => string.Equals(x.Name, parameterInfo.Name, StringComparison.OrdinalIgnoreCase)); | |||
if (arg == default) | |||
return parameterInfo.IsRequired ? ExecuteResult.FromError(InteractionCommandError.BadArgs, "Command was invoked with too few parameters") : | |||
ParseResult.FromSuccess(parameterInfo.DefaultValue); | |||
TypeConverterResult.FromSuccess(parameterInfo.DefaultValue); | |||
var typeConverter = parameterInfo.TypeConverter; | |||
var readResult = await typeConverter.ReadAsync(context, arg, services).ConfigureAwait(false); | |||
if (!readResult.IsSuccess) | |||
return readResult; | |||
return ParseResult.FromSuccess(readResult.Value); | |||
return readResult; | |||
} | |||
protected override Task InvokeModuleEvent (IInteractionContext context, IResult result) | |||
@@ -103,7 +103,7 @@ namespace Discord.Interactions | |||
public async Task<IResult> CreateModalAsync(IInteractionContext context, IServiceProvider services = null, bool throwOnMissingField = false) | |||
{ | |||
if (context.Interaction is not IModalInteraction modalInteraction) | |||
return ParseResult.FromError(InteractionCommandError.Unsuccessful, "Provided context doesn't belong to a Modal Interaction."); | |||
return TypeConverterResult.FromError(InteractionCommandError.Unsuccessful, "Provided context doesn't belong to a Modal Interaction."); | |||
services ??= EmptyServiceProvider.Instance; | |||
@@ -120,7 +120,7 @@ namespace Discord.Interactions | |||
if (!throwOnMissingField) | |||
args[i] = input.DefaultValue; | |||
else | |||
return ParseResult.FromError(InteractionCommandError.BadArgs, $"Modal interaction is missing the required field: {input.CustomId}"); | |||
return TypeConverterResult.FromError(InteractionCommandError.BadArgs, $"Modal interaction is missing the required field: {input.CustomId}"); | |||
} | |||
else | |||
{ | |||
@@ -133,7 +133,7 @@ namespace Discord.Interactions | |||
} | |||
} | |||
return ParseResult.FromSuccess(_initializer(args)); | |||
return TypeConverterResult.FromSuccess(_initializer(args)); | |||
} | |||
} | |||
} |
@@ -822,7 +822,7 @@ namespace Discord.Interactions | |||
SetMatchesIfApplicable(context, result); | |||
return await result.Command.ExecuteAsync(context, services, result.RegexCaptureGroups).ConfigureAwait(false); | |||
return await result.Command.ExecuteAsync(context, services).ConfigureAwait(false); | |||
} | |||
private async Task<IResult> ExecuteAutocompleteAsync (IInteractionContext context, IAutocompleteInteraction interaction, IServiceProvider services ) | |||
@@ -869,7 +869,7 @@ namespace Discord.Interactions | |||
SetMatchesIfApplicable(context, result); | |||
return await result.Command.ExecuteAsync(context, services, result.RegexCaptureGroups).ConfigureAwait(false); | |||
return await result.Command.ExecuteAsync(context, services).ConfigureAwait(false); | |||
} | |||
private static void SetMatchesIfApplicable<T>(IInteractionContext context, SearchResult<T> searchResult) | |||
@@ -2,9 +2,9 @@ using System; | |||
namespace Discord.Interactions | |||
{ | |||
internal struct ParseResult : IResult | |||
public struct ParseResult : IResult | |||
{ | |||
public object Value { get; } | |||
public object[] Args { get; } | |||
public InteractionCommandError? Error { get; } | |||
@@ -12,15 +12,15 @@ namespace Discord.Interactions | |||
public bool IsSuccess => !Error.HasValue; | |||
private ParseResult(object value, InteractionCommandError? error, string reason) | |||
private ParseResult(object[] args, InteractionCommandError? error, string reason) | |||
{ | |||
Value = value; | |||
Args = args; | |||
Error = error; | |||
ErrorReason = reason; | |||
} | |||
public static ParseResult FromSuccess(object value) => | |||
new ParseResult(value, null, null); | |||
public static ParseResult FromSuccess(object[] args) => | |||
new ParseResult(args, null, null); | |||
public static ParseResult FromError(Exception exception) => | |||
new ParseResult(null, InteractionCommandError.Exception, exception.Message); | |||