public static ParseResult FromSuccess(IReadOnlyList <TypeReaderValue> argValues, IReadOnlyList <TypeReaderValue> paramValues) { var argList = new TypeReaderResult[argValues.Count]; for (int i = 0; i < argValues.Count; i++) { argList[i] = TypeReaderResult.FromSuccess(argValues[i]); } TypeReaderResult[] paramList = null; if (paramValues != null) { paramList = new TypeReaderResult[paramValues.Count]; for (int i = 0; i < paramValues.Count; i++) { paramList[i] = TypeReaderResult.FromSuccess(paramValues[i]); } } return(new ParseResult(argList, paramList, null, null)); }
public override async Task <TypeReaderResult> ReadAsync(ITurnContext context, string input, IServiceProvider services) { var result = new T(); var state = ReadState.LookingForParameter; int beginRead = 0, currentRead = 0; while (state != ReadState.End) { try { var prop = Read(out var arg); var propVal = await ReadArgumentAsync(prop, arg).ConfigureAwait(false); if (propVal != null) { prop.SetMethod.Invoke(result, new[] { propVal }); } else { return(TypeReaderResult.FromError(CommandError.ParseFailed, $"Could not parse the argument for the parameter '{prop.Name}' as type '{prop.PropertyType}'.")); } } catch (Exception ex) { //TODO: use the Exception overload after a rebase on latest return(TypeReaderResult.FromError(CommandError.Exception, ex.Message)); } } return(TypeReaderResult.FromSuccess(result)); PropertyInfo Read(out string arg) { string currentParam = null; char match = '\0'; for (; currentRead < input.Length; currentRead++) { var currentChar = input[currentRead]; switch (state) { case ReadState.LookingForParameter: if (Char.IsWhiteSpace(currentChar)) { continue; } else { beginRead = currentRead; state = ReadState.InParameter; } break; case ReadState.InParameter: if (currentChar != ':') { continue; } else { currentParam = input.Substring(beginRead, currentRead - beginRead); state = ReadState.LookingForArgument; } break; case ReadState.LookingForArgument: if (Char.IsWhiteSpace(currentChar)) { continue; } else { beginRead = currentRead; state = (QuotationAliasUtils.GetDefaultAliasMap.TryGetValue(currentChar, out match)) ? ReadState.InQuotedArgument : ReadState.InArgument; } break; case ReadState.InArgument: if (!Char.IsWhiteSpace(currentChar)) { continue; } else { return(GetPropAndValue(out arg)); } case ReadState.InQuotedArgument: if (currentChar != match) { continue; } else { return(GetPropAndValue(out arg)); } } } if (currentParam == null) { throw new InvalidOperationException("No parameter name was read."); } return(GetPropAndValue(out arg)); PropertyInfo GetPropAndValue(out string argv) { bool quoted = state == ReadState.InQuotedArgument; state = (currentRead == (quoted ? input.Length - 1 : input.Length)) ? ReadState.End : ReadState.LookingForParameter; if (quoted) { argv = input.Substring(beginRead + 1, currentRead - beginRead - 1).Trim(); currentRead++; } else { argv = input.Substring(beginRead, currentRead - beginRead); } return(_tProps[currentParam]); } } async Task <object> ReadArgumentAsync(PropertyInfo prop, string arg) { var elemType = prop.PropertyType; bool isCollection = false; if (elemType.GetTypeInfo().IsGenericType&& elemType.GetGenericTypeDefinition() == typeof(IEnumerable <>)) { elemType = prop.PropertyType.GenericTypeArguments[0]; isCollection = true; } var overridden = prop.GetCustomAttribute <OverrideTypeReaderAttribute>(); var reader = (overridden != null) ? ModuleClassBuilder.GetTypeReader(_commands, elemType, overridden.TypeReader, services) : (_commands.GetDefaultTypeReader(elemType) ?? _commands.GetTypeReaders(elemType).FirstOrDefault().Value); if (reader != null) { if (isCollection) { var method = _readMultipleMethod.MakeGenericMethod(elemType); var task = (Task <IEnumerable>)method.Invoke(null, new object[] { reader, context, arg.Split(','), services }); return(await task.ConfigureAwait(false)); } else { return(await ReadSingle(reader, context, arg, services).ConfigureAwait(false)); } } return(null); } }
public static async Task <ParseResult> ParseArgsAsync(CommandInfo command, ITurnContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos, IReadOnlyDictionary <char, char> aliasMap) { ParameterInfo curParam = null; StringBuilder argBuilder = new StringBuilder(input.Length); int endPos = input.Length; var curPart = ParserPart.None; int lastArgEndPos = int.MinValue; var argList = ImmutableArray.CreateBuilder <TypeReaderResult>(); var paramList = ImmutableArray.CreateBuilder <TypeReaderResult>(); bool isEscaping = false; char c, matchQuote = '\0'; // local helper functions bool IsOpenQuote(IReadOnlyDictionary <char, char> dict, char ch) { // return if the key is contained in the dictionary if it is populated if (dict.Count != 0) { return(dict.ContainsKey(ch)); } // or otherwise if it is the default double quote return(c == '\"'); } char GetMatch(IReadOnlyDictionary <char, char> dict, char ch) { // get the corresponding value for the key, if it exists // and if the dictionary is populated if (dict.Count != 0 && dict.TryGetValue(c, out var value)) { return(value); } // or get the default pair of the default double quote return('\"'); } for (int curPos = startPos; curPos <= endPos; curPos++) { if (curPos < endPos) { c = input[curPos]; } else { c = '\0'; } //If this character is escaped, skip it if (isEscaping) { if (curPos != endPos) { argBuilder.Append(c); isEscaping = false; continue; } } //Are we escaping the next character? if (c == '\\' && (curParam == null || !curParam.IsRemainder)) { isEscaping = true; continue; } //If we're processing an remainder parameter, ignore all other logic if (curParam != null && curParam.IsRemainder && curPos != endPos) { argBuilder.Append(c); continue; } //If we're not currently processing one, are we starting the next argument yet? if (curPart == ParserPart.None) { if (char.IsWhiteSpace(c) || curPos == endPos) { continue; //Skip whitespace between arguments } else if (curPos == lastArgEndPos) { return(ParseResult.FromError(CommandError.ParseFailed, "There must be at least one character of whitespace between arguments.")); } else { if (curParam == null) { curParam = command.Parameters.Count > argList.Count ? command.Parameters[argList.Count] : null; } if (curParam != null && curParam.IsRemainder) { argBuilder.Append(c); continue; } if (IsOpenQuote(aliasMap, c)) { curPart = ParserPart.QuotedParameter; matchQuote = GetMatch(aliasMap, c); continue; } curPart = ParserPart.Parameter; } } //Has this parameter ended yet? string argString = null; if (curPart == ParserPart.Parameter) { if (curPos == endPos || char.IsWhiteSpace(c)) { argString = argBuilder.ToString(); lastArgEndPos = curPos; } else { argBuilder.Append(c); } } else if (curPart == ParserPart.QuotedParameter) { if (c == matchQuote) { argString = argBuilder.ToString(); //Remove quotes lastArgEndPos = curPos + 1; } else { argBuilder.Append(c); } } if (argString != null) { if (curParam == null) { if (command.IgnoreExtraArgs) { break; } else { return(ParseResult.FromError(CommandError.BadArgCount, "The input text has too many parameters.")); } } var typeReaderResult = await curParam.ParseAsync(context, argString, services).ConfigureAwait(false); if (!typeReaderResult.IsSuccess && typeReaderResult.Error != CommandError.MultipleMatches) { return(ParseResult.FromError(typeReaderResult)); } if (curParam.IsMultiple) { paramList.Add(typeReaderResult); curPart = ParserPart.None; } else { argList.Add(typeReaderResult); curParam = null; curPart = ParserPart.None; } argBuilder.Clear(); } } if (curParam != null && curParam.IsRemainder) { var typeReaderResult = await curParam.ParseAsync(context, argBuilder.ToString(), services).ConfigureAwait(false); if (!typeReaderResult.IsSuccess) { return(ParseResult.FromError(typeReaderResult)); } argList.Add(typeReaderResult); } if (isEscaping) { return(ParseResult.FromError(CommandError.ParseFailed, "Input text may not end on an incomplete escape.")); } if (curPart == ParserPart.QuotedParameter) { return(ParseResult.FromError(CommandError.ParseFailed, "A quoted parameter is incomplete.")); } //Add missing optionals for (int i = argList.Count; i < command.Parameters.Count; i++) { var param = command.Parameters[i]; if (param.IsMultiple) { continue; } if (!param.IsOptional) { return(ParseResult.FromError(CommandError.BadArgCount, "The input text has too few parameters.")); } argList.Add(TypeReaderResult.FromSuccess(param.DefaultValue)); } return(ParseResult.FromSuccess(argList.ToImmutable(), paramList.ToImmutable())); }
/// <inheritdoc /> public override Task <TypeReaderResult> ReadAsync(ITurnContext context, string input, IServiceProvider services) { return((TimeSpan.TryParseExact(input.ToLowerInvariant(), Formats, CultureInfo.InvariantCulture, out var timeSpan)) ? Task.FromResult(TypeReaderResult.FromSuccess(timeSpan)) : Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse TimeSpan"))); }