/// <summary> /// Executes the command. /// </summary> /// <param name="context">The context of the command.</param> /// <param name="input">The command string.</param> /// <param name="services">The service to be used in the command's dependency injection.</param> /// <param name="multiMatchHandling">The handling mode when multiple command matches are found.</param> /// <returns> /// A task that represents the asynchronous execution operation. The task result contains the result of the /// command execution. /// </returns> public async Task <IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) { services = services ?? EmptyServiceProvider.Instance; var searchResult = Search(input); if (!searchResult.IsSuccess) { return(searchResult); } var commands = searchResult.Commands; var preconditionResults = new Dictionary <CommandMatch, PreconditionResult>(); foreach (var match in commands) { preconditionResults[match] = await match.Command.CheckPreconditionsAsync(context, services).ConfigureAwait(false); } var successfulPreconditions = preconditionResults .Where(x => x.Value.IsSuccess) .ToArray(); if (successfulPreconditions.Length == 0) { //All preconditions failed, return the one from the highest priority command var bestCandidate = preconditionResults .OrderByDescending(x => x.Key.Command.Priority) .FirstOrDefault(x => !x.Value.IsSuccess); return(bestCandidate.Value); } //If we get this far, at least one precondition was successful. var parseResultsDict = new Dictionary <CommandMatch, ParseResult>(); foreach (var pair in successfulPreconditions) { var parseResult = await pair.Key.ParseAsync(context, searchResult, pair.Value, services).ConfigureAwait(false); if (parseResult.Error == CommandError.MultipleMatches) { IReadOnlyList <TypeReaderValue> argList, paramList; switch (multiMatchHandling) { case MultiMatchHandling.Best: argList = parseResult.ArgValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToImmutableArray(); paramList = parseResult.ParamValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToImmutableArray(); parseResult = ParseResult.FromSuccess(argList, paramList); break; } } parseResultsDict[pair.Key] = parseResult; } // Calculates the 'score' of a command given a parse result float CalculateScore(CommandMatch match, ParseResult parseResult) { float argValuesScore = 0, paramValuesScore = 0; if (match.Command.Parameters.Count > 0) { var argValuesSum = parseResult.ArgValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; var paramValuesSum = parseResult.ParamValues?.Sum(x => x.Values.OrderByDescending(y => y.Score).FirstOrDefault().Score) ?? 0; argValuesScore = argValuesSum / match.Command.Parameters.Count; paramValuesScore = paramValuesSum / match.Command.Parameters.Count; } var totalArgsScore = (argValuesScore + paramValuesScore) / 2; return(match.Command.Priority + totalArgsScore * 0.99f); } //Order the parse results by their score so that we choose the most likely result to execute var parseResults = parseResultsDict .OrderByDescending(x => CalculateScore(x.Key, x.Value)); var successfulParses = parseResults .Where(x => x.Value.IsSuccess) .ToArray(); if (successfulParses.Length == 0) { //All parses failed, return the one from the highest priority command, using score as a tie breaker var bestMatch = parseResults .FirstOrDefault(x => !x.Value.IsSuccess); return(bestMatch.Value); } //If we get this far, at least one parse was successful. Execute the most likely overload. var chosenOverload = successfulParses[0]; return(await chosenOverload.Key.ExecuteAsync(context, chosenOverload.Value, services).ConfigureAwait(false)); }