Ejemplo n.º 1
0
        /// <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));
        }