Ejemplo n.º 1
0
        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)));
            }
        }
Ejemplo n.º 2
0
        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];
Ejemplo n.º 4
0
        public override async Task <ArgsParseResult <OneOf> > Parse(
            IImmutableList <string> args,
            Type[] genericTypes)
        {
            var failures = new List <Failure>();

            for (int i = 0; i < genericTypes.Length; i++)
            {
                Type nestedType = genericTypes[i];
                ArgsParseResult <List <object> > parseResult = await _argsParser.ParseRaw(args, new[] { nestedType });

                if (parseResult.SuccessResult == null)
                {
                    Debug.Assert(parseResult.Failures.Any());
                    failures.AddRange(parseResult.Failures);
                    continue;
                }
                object result = parseResult.SuccessResult.Value.Result.First();
                Type   type   = genericTypes.Length switch
                {
                    2 => typeof(OneOf <,>),
                    3 => typeof(OneOf <, ,>),
                    4 => typeof(OneOf <, , ,>),
                    var num => throw new InvalidOperationException(
                              $"An implementation of {typeof(OneOf)} for {num} generic arguments " +
                              "needs to be implemented and wired up where this exception is thrown. " +
                              "But do you _really_ want \"one of\" this many arguments?")
                };
                ConstructorInfo?oneOfConstructor = type
                                                   .MakeGenericType(genericTypes)
                                                   .GetConstructor(genericTypes.Select(t => typeof(Optional <>).MakeGenericType(t)).ToArray());
                if (oneOfConstructor == null)
                {
                    throw new InvalidOperationException(
                              $"{type} needs a constructor with {genericTypes.Length} parameters.");
                }
                object[] invokeArgs = new object[genericTypes.Length];
                for (int j = 0; j < invokeArgs.Length; j++)
                {
                    // create Optional<> instances. their constructor takes (bool success, object value)
                    Type            genericType         = genericTypes[j];
                    ConstructorInfo?optionalConstructor = typeof(Optional <>)
                                                          .MakeGenericType(genericType)
                                                          .GetConstructor(new[] { typeof(bool), genericType });
                    if (optionalConstructor == null)
                    {
                        throw new InvalidOperationException($"{typeof(Optional<>)} needs a constructor (bool, object)");
                    }
                    invokeArgs[j] = i == j // for the successful one, fill the optional with the result
                        ? optionalConstructor.Invoke(new[] { true, result })
                        : optionalConstructor.Invoke(new object[] { false, null ! });
Ejemplo n.º 5
0
        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()));
        }
Ejemplo n.º 6
0
        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));
        }
Ejemplo n.º 7
0
        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")));
            }
        }
Ejemplo n.º 8
0
        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")));
        }
Ejemplo n.º 9
0
        public override async Task <ArgsParseResult <ManyOf> > Parse(
            IImmutableList <string> args,
            Type[] genericTypes)
        {
            if (genericTypes.Length != 1)
            {
                throw new ArgumentException("list parser must receive exactly 1 generic type", nameof(genericTypes));
            }
            Type listContentType = genericTypes[0];

            Type            manyOfType  = typeof(ManyOf <>).MakeGenericType(listContentType);
            ConstructorInfo?constructor = manyOfType.GetConstructor(new[] { typeof(IEnumerable <object>) });

            if (constructor == null)
            {
                throw new InvalidOperationException(
                          $"{manyOfType} needs a constructor (IEnumerable<object> values).");
            }

            List <Failure> failures = new();

            for (int numArgs = args.Count; numArgs > 0; numArgs--)
            {
                ArgsParseResult <List <object> > result = await _argsParser
                                                          .ParseRaw(args, Enumerable.Repeat(listContentType, numArgs));

                failures.AddRange(result.Failures);
                if (result.SuccessResult != null)
                {
                    List <object> contents = result.SuccessResult.Value.Result;
                    return(ArgsParseResult <ManyOf> .Success(
                               failures.ToImmutableList(),
                               (ManyOf)constructor.Invoke(new object[] { contents }),
                               result.SuccessResult.Value.RemainingArgs
                               ));
                }
            }
            return(ArgsParseResult <ManyOf> .Success(
                       failures.ToImmutableList(),
                       (ManyOf)constructor.Invoke(new object?[] { ImmutableList.Create <object>() }),
                       args
                       ));
        }
Ejemplo n.º 10
0
        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()));
        }
    }
Ejemplo n.º 11
0
        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')")));
            }
        }
Ejemplo n.º 12
0
        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)
                                   ));
        }
Ejemplo n.º 13
0
        public override async Task <ArgsParseResult <Optional> > Parse(
            IImmutableList <string> args,
            Type[] genericTypes)
        {
            if (genericTypes.Length != 1)
            {
                throw new ArgumentException($"Only expected 1 generic argument for {typeof(Optional)}, " +
                                            $"but got {genericTypes.Length}");
            }
            var             type        = typeof(Optional <>).MakeGenericType(genericTypes[0]);
            ConstructorInfo?constructor = type.GetConstructor(new[] { typeof(bool), genericTypes[0] });

            if (constructor == null)
            {
                throw new InvalidOperationException($"{type} needs a constructor (bool present, T value).");
            }

            if (!args.Any())
            {
                var optional = (Optional)constructor.Invoke(new object?[] { false, null });
                return(ArgsParseResult <Optional> .Success(optional, args));
            }
            ArgsParseResult <List <object> > parseResult = await _argsParser.ParseRaw(args, genericTypes);

            if (parseResult.SuccessResult != null)
            {
                Success <List <object> > success = parseResult.SuccessResult.Value;
                var optional = (Optional)constructor.Invoke(new[] { true, success.Result[0] });
                return(ArgsParseResult <Optional> .Success(parseResult.Failures, optional, success.RemainingArgs));
            }
            else
            {
                var optional = (Optional)constructor.Invoke(new object?[] { false, null });
                return(ArgsParseResult <Optional> .Success(parseResult.Failures, optional, args));
            }
        }
Ejemplo n.º 14
0
        public override Task <ArgsParseResult <string> > Parse(IImmutableList <string> args, Type[] genericTypes)
        {
            ArgsParseResult <string> result = ArgsParseResult <string> .Success(args[0], args.Skip(1).ToImmutableList());

            return(Task.FromResult(result));
        }
Ejemplo n.º 15
0
        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()));
        }
    }