public override Task <ArgsParseResult <int> > Parse(IImmutableList <string> args, Type[] genericTypes) { string str = args[0]; try { int number = int.Parse(str); if (number < _minValue) { return(Task.FromResult(ArgsParseResult <int> .Failure( $"'{str}' cannot be below {_minValue}", ErrorRelevanceConfidence.Likely))); } if (number > _maxValue) { return(Task.FromResult(ArgsParseResult <int> .Failure( $"'{str}' cannot be above {_maxValue}", ErrorRelevanceConfidence.Likely))); } ArgsParseResult <int> result = ArgsParseResult <int> .Success(number, args.Skip(1).ToImmutableList()); return(Task.FromResult(result)); } catch (FormatException) { return(Task.FromResult(ArgsParseResult <int> .Failure($"did not recognize '{str}' as a number"))); } catch (OverflowException) { return(Task.FromResult(ArgsParseResult <int> .Failure( $"'{str}' is out of range", ErrorRelevanceConfidence.Likely))); } }
public override Task <ArgsParseResult <T> > Parse(IImmutableList <string> args, Type[] genericTypes) { string str = args[0]; var match = _regex.Match(str); if (!match.Success) { return(Task.FromResult(ArgsParseResult <T> .Failure( $"did not recognize '{str}' as a '{_prefix}'-prefixed number"))); } try { int number = int.Parse(match.Groups["number"].Value); if (number < _minValue) { return(Task.FromResult(ArgsParseResult <T> .Failure( $"'{str}' cannot be less than {_prefix}{_minValue}", ErrorRelevanceConfidence.Likely))); } if (number > _maxValue) { return(Task.FromResult(ArgsParseResult <T> .Failure( $"'{str}' cannot be more than {_prefix}{_maxValue}", ErrorRelevanceConfidence.Likely))); } var value = new T { Number = number }; return(Task.FromResult(ArgsParseResult <T> .Success(value, args.Skip(1).ToImmutableList()))); } catch (OverflowException) { return(Task.FromResult(ArgsParseResult <T> .Failure( $"'{str}' is out of range", ErrorRelevanceConfidence.Likely))); } }
public override Task <ArgsParseResult <Percentage> > Parse(IImmutableList <string> args, Type[] genericTypes) { string percentageStr = args[0]; if (!percentageStr.EndsWith('%')) { return(Task.FromResult(ArgsParseResult <Percentage> .Failure("percentages must end in '%'"))); } string doubleStr = percentageStr[..^ 1];
public override async Task <ArgsParseResult <User> > Parse(IImmutableList <string> args, Type[] genericTypes) { string simpleName = args[0].ToLower(); bool isPrefixed = simpleName.StartsWith('@'); if (isPrefixed) { simpleName = simpleName.Substring(1); } User?user = await _userRepo.FindBySimpleName(simpleName); return(user == null ? ArgsParseResult <User> .Failure($"did not recognize a user with the name '{simpleName}'", isPrefixed?ErrorRelevanceConfidence.Likely : ErrorRelevanceConfidence.Default) : ArgsParseResult <User> .Success(user, args.Skip(1).ToImmutableList())); }
public override Task <ArgsParseResult <Role> > Parse(IImmutableList <string> args, Type[] genericTypes) { string roleToParse = args[0]; ArgsParseResult <Role> result; try { Role parsedRole = (Role)Enum.Parse(typeof(Role), roleToParse, ignoreCase: true); result = ArgsParseResult <Role> .Success(parsedRole, args.Skip(1).ToImmutableList()); } catch (ArgumentException) { result = ArgsParseResult <Role> .Failure($"Did not find a role named '{roleToParse}'"); } return(Task.FromResult(result)); }
public override Task <ArgsParseResult <HexColor> > Parse(IImmutableList <string> args, Type[] genericTypes) { Match colorMatch = _regex.Match(args[0]); if (colorMatch.Success) { var color = new HexColor(colorMatch.Value.ToUpper()); return(Task.FromResult(ArgsParseResult <HexColor> .Success(color, args.Skip(1).ToImmutableList()))); } else { return(Task.FromResult(args[0].StartsWith("#") ? ArgsParseResult <HexColor> .Failure( $"'{args[0]}' must be a 6-character hex code consisting of 0-9 and A-F, " + "for example '#FF0000' for pure red.", ErrorRelevanceConfidence.Likely) : ArgsParseResult <HexColor> .Failure($"'{args[0]}' is not a valid hex color"))); } }
public override Task <ArgsParseResult <Instant> > Parse(IImmutableList <string> args, Type[] genericTypes) { if (args.Count >= 2) { // try with 2 arguments first, in case it contains a space instead of 'T' string str = $"{args[0]}T{args[1]}"; ParseResult <Instant> resultFromTwoArgs = InstantPattern.ExtendedIso.Parse(str); if (resultFromTwoArgs.Success) { return(Task.FromResult(ArgsParseResult <Instant> .Success( resultFromTwoArgs.Value, args.Skip(2).ToImmutableList()))); } } ParseResult <Instant> resultFromOneArgs = InstantPattern.ExtendedIso.Parse(args[0]); return(Task.FromResult(resultFromOneArgs.Success ? ArgsParseResult <Instant> .Success(resultFromOneArgs.Value, args.Skip(1).ToImmutableList()) : ArgsParseResult <Instant> .Failure($"did not recognize '{args[0]}' as a UTC-instant"))); }
public override async Task <ArgsParseResult <AnyOrder> > Parse( IImmutableList <string> args, Type[] genericTypes) { var argList = args.Take(genericTypes.Length).ToList(); var failures = new List <Failure>(); foreach (var argsPermutation in Permutations(argList)) { ArgsParseResult <List <object> > parseResult = await _argsParser .ParseRaw(argsPermutation.ToImmutableList(), genericTypes); if (parseResult.SuccessResult == null) { Debug.Assert(parseResult.Failures.Any()); failures.AddRange(parseResult.Failures); continue; } List <object> items = parseResult.SuccessResult.Value.Result; Type type = items.Count switch { 2 => typeof(AnyOrder <,>), 3 => typeof(AnyOrder <, ,>), 4 => typeof(AnyOrder <, , ,>), var num => throw new InvalidOperationException( $"An implementation of {typeof(AnyOrder)} for {num} generic arguments " + "needs to be implemented and wired up where this exception is thrown. " + "But do you _really_ want this many arguments in any order?") }; ConstructorInfo?constructor = type.MakeGenericType(genericTypes).GetConstructor(genericTypes); if (constructor == null) { throw new InvalidOperationException($"{type} needs a constructor with {items.Count} parameters."); } return(ArgsParseResult <AnyOrder> .Success( parseResult.Failures, (AnyOrder)constructor.Invoke(items.ToArray()), parseResult.SuccessResult.Value.RemainingArgs)); } Debug.Assert(failures.Any()); return(ArgsParseResult <AnyOrder> .Failure(failures.ToImmutableList())); } }
public override Task <ArgsParseResult <TimeSpan> > Parse(IImmutableList <string> args, Type[] genericTypes) { string str = args[0]; var match = Regex.Match(str); if (match.Success) { try { string weeks = match.Groups["weeks"].Value; string days = match.Groups["days"].Value; string hours = match.Groups["hours"].Value; string minutes = match.Groups["minutes"].Value; string seconds = match.Groups["seconds"].Value; var timeSpan = new TimeSpan( days: (weeks.Length > 0 ? int.Parse(weeks) : 0) * 7 + (days.Length > 0 ? int.Parse(days) : 0), hours: hours.Length > 0 ? int.Parse(hours) : 0, minutes: minutes.Length > 0 ? int.Parse(minutes) : 0, seconds: seconds.Length > 0 ? int.Parse(seconds) : 0 ); return(Task.FromResult(ArgsParseResult <TimeSpan> .Success( timeSpan, args.Skip(1).ToImmutableList()))); } catch (FormatException) { return(Task.FromResult(ArgsParseResult <TimeSpan> .Failure( $"did not recognize '{str}' as a duration"))); } catch (OverflowException) { return(Task.FromResult(ArgsParseResult <TimeSpan> .Failure( $"the duration described by '{str}' is out of range", ErrorRelevanceConfidence.Likely))); } } else { return(Task.FromResult(ArgsParseResult <TimeSpan> .Failure( $"did not recognize '{str}' as a duration in the form " + "'<weeks>w<days>d<hours>h<minutes>m<seconds>s' (zeroes may be omitted, e.g. '1d12h')"))); } }
public override Task <ArgsParseResult <PkmnSpecies> > Parse(IImmutableList <string> args, Type[] genericTypes) { if (!args[0].StartsWith("#")) { string normalizedName = NormalizeName(args[0]); if (_nameLookup.TryGetValue(normalizedName, out PkmnSpecies? speciesFromName)) { return(Task.FromResult(ArgsParseResult <PkmnSpecies> .Success( speciesFromName !, args.Skip(1).ToImmutableList()))); } if (args.Count >= 2) { string normalizedNameTwoArgs = NormalizeName(args[0] + ' ' + args[1]); if (_nameLookup.TryGetValue(normalizedNameTwoArgs, out PkmnSpecies? speciesFromTwoArgsName)) { return(Task.FromResult(ArgsParseResult <PkmnSpecies> .Success( speciesFromTwoArgsName, args.Skip(2).ToImmutableList()))); } } if (int.TryParse(normalizedName, out _)) { return(Task.FromResult(ArgsParseResult <PkmnSpecies> .Failure( "Please prefix with '#' to supply and pokedex number", ErrorRelevanceConfidence.Likely))); } else { return(Task.FromResult(ArgsParseResult <PkmnSpecies> .Failure( $"No pokemon with the name '{normalizedName}' was recognized. " + "Please supply a valid name, or prefix with '#' to supply and pokedex number instead"))); } } string speciesId = args[0].Substring(startIndex: 1); return(Task.FromResult(_idLookup.TryGetValue(speciesId.TrimStart('0'), out var species) ? ArgsParseResult <PkmnSpecies> .Success(species, args.Skip(1).ToImmutableList()) : ArgsParseResult <PkmnSpecies> .Failure($"did not recognize species '{args[0]}'", ErrorRelevanceConfidence.Likely) )); }
public override async Task <ArgsParseResult <AnyOrder> > Parse( IImmutableList <string> args, Type[] genericTypes) { var failures = new List <Failure>(); ArgsParseResult <AnyOrder>?mostSpecificSuccess = null; foreach (IList <int> permutationIndexes in Permutations(Enumerable.Range(0, genericTypes.Length).ToList())) { ArgsParseResult <List <object> > parseResult = await _argsParser .ParseRaw(args, permutationIndexes.Select(i => genericTypes[i])); if (parseResult.SuccessResult == null) { Debug.Assert(parseResult.Failures.Any()); failures.AddRange(parseResult.Failures); continue; } if (mostSpecificSuccess != null && mostSpecificSuccess.Value.SuccessResult !.Value.RemainingArgs.Count <= parseResult.SuccessResult.Value.RemainingArgs.Count) { // if there already is an equally or more specific result, don't even bother processing this one continue; } List <object> items = parseResult.SuccessResult.Value.Result; Type type = items.Count switch { 2 => typeof(AnyOrder <,>), 3 => typeof(AnyOrder <, ,>), 4 => typeof(AnyOrder <, , ,>), var num => throw new InvalidOperationException( $"An implementation of {typeof(AnyOrder)} for {num} generic arguments " + "needs to be implemented and wired up where this exception is thrown. " + "But do you _really_ want this many arguments in any order?") }; ConstructorInfo?constructor = type.MakeGenericType(genericTypes).GetConstructor(genericTypes); if (constructor == null) { throw new InvalidOperationException($"{type} needs a constructor with {items.Count} parameters."); } // If our permutation is [2,0,1] and our result is [c,a,b], we need to restore the // arguments' original order [a,b,c] before passing them to the AnyOrder constructor. object?[] itemsUnshuffled = permutationIndexes.Zip(items) .OrderBy(tpl => tpl.First) .Select(tpl => tpl.Second) .ToArray(); mostSpecificSuccess = ArgsParseResult <AnyOrder> .Success( parseResult.Failures, (AnyOrder)constructor.Invoke(itemsUnshuffled), parseResult.SuccessResult.Value.RemainingArgs); if (parseResult.SuccessResult.Value.RemainingArgs.Count == 0) { break; // we won't get any better than this } } if (mostSpecificSuccess != null) { return(mostSpecificSuccess.Value); } Debug.Assert(failures.Any()); return(ArgsParseResult <AnyOrder> .Failure(failures.ToImmutableList())); } }