/// <inheritdoc/> public async Task <ICommandResult> ExecuteAsync(ICommandContext context, IServiceProvider services, ICommandResult matchResult, object handler, CancellationToken cancellationToken = default) { // ensure provided check result is valid if (matchResult == null) { throw new ArgumentNullException(nameof(matchResult)); } if (!matchResult.IsSuccess) { return(CommandExecutionResult.Failure); } if (!(matchResult is RegexCommandMatchResult regexMatchResult)) { throw new ArgumentException($"{nameof(matchResult)} must be of type {typeof(RegexCommandMatchResult).FullName}", nameof(matchResult)); } // run all custom attributes foreach (ICommandRequirement check in this.Requirements) { if (!await check.CheckAsync(context, services, cancellationToken).ConfigureAwait(false)) { return(new CommandExecutionResult(false, new string[] { check.ErrorMessage }, null)); } } // build params cancellationToken.ThrowIfCancellationRequested(); IParameterBuilder paramBuilder = services.GetRequiredService <IParameterBuilder>(); ParameterBuilderValues paramBuilderValues = new ParameterBuilderValues { Args = regexMatchResult.RegexMatch.Groups.Cast <Group>().Skip(1) .Select(s => s.Value ?? string.Empty).ToArray(), ArgumentConverterProvider = services.GetService <IArgumentConverterProvider>(), CancellationToken = cancellationToken, Context = context, Services = services, CommandInstance = this, AdditionalObjects = new object[] { regexMatchResult.RegexMatch } }; ParameterBuildingResult paramsResult = await paramBuilder.BuildParamsAsync(_params, paramBuilderValues, cancellationToken).ConfigureAwait(false); if (!paramsResult.IsSuccess) { return(paramsResult); } // execute - if it's a task, await it cancellationToken.ThrowIfCancellationRequested(); if (this.Method.Invoke(handler, paramsResult.Values) is Task returnTask) { await returnTask.ConfigureAwait(false); } return(CommandExecutionResult.Success); }
/// <inheritdoc/> public async Task <ParameterBuildingResult> BuildParamsAsync(IEnumerable <ParameterInfo> parameters, ParameterBuilderValues values, CancellationToken cancellationToken = default) { if (values == null) { throw new ArgumentNullException(nameof(values)); } if (parameters?.Any() != true) { return(ParameterBuildingResult.Success(Array.Empty <object>())); } object[] paramsValues = new object[parameters.Count()]; int argIndex = 0; // parse arg index, so they're handled in order foreach (ParameterInfo param in parameters) { cancellationToken.ThrowIfCancellationRequested(); // check additionals first, in case they override something if (TryFindAdditional(param.ParameterType, values.AdditionalObjects, out object value)) { } // from context else if (values.Context != null && param.ParameterType.IsAssignableFrom(values.Context.GetType())) { value = values.Context; } else if (values.Context != null && param.ParameterType.IsAssignableFrom(values.Context.Message.GetType())) { value = values.Context.Message; } else if (values.Context != null && param.ParameterType.IsAssignableFrom(values.Context.Client.GetType())) { value = values.Context.Client; } // command instance else if (values.CommandInstance != null && param.ParameterType.IsAssignableFrom(values.CommandInstance.GetType())) { value = values.CommandInstance; } // cancellation token else if (param.ParameterType.IsAssignableFrom(typeof(CancellationToken))) { value = values.CancellationToken; } // from services else if (TryGetService(param.ParameterType, values.Services, out value)) { } // logger from factory else if (TryGetGenericLogger(param.ParameterType, values.Services, out value)) { } // from args else { // try to convert as arg if (TryConvertArgument(param, argIndex, values, out value, out Exception convertingError)) { argIndex++; } // if there's an error, let's return result with message - but without exception, as we don't want input errors to be logged else if (convertingError != null) { return(ParameterBuildingResult.Failure(null, new string[] { await param.GetConvertingErrorAttribute().ToStringAsync(values.Context, values.Args[argIndex], param, cancellationToken).ConfigureAwait(false) })); } // if it's optional, just let it pass else if (param.IsOptional) { value = param.HasDefaultValue ? param.DefaultValue : null; } // if not default and not thrown conversion error, but still not found yet - means it's arg that is expected, but user didn't provide it in command - so return error with message - do not provide exception, as we don't want it logged else if (argIndex <= values.Args.Length) { return(ParameterBuildingResult.Failure(null, new string[] { await param.GetMissingErrorAttribute().ToStringAsync(values.Context, values.Args.Length > argIndex ? values.Args[argIndex] : string.Empty, param, cancellationToken).ConfigureAwait(false) })); } // none found, throw else { throw new InvalidOperationException($"Unsupported param type: {param.ParameterType.FullName}"); } } paramsValues[param.Position] = value; } return(ParameterBuildingResult.Success(paramsValues)); }