Esempio n. 1
0
        public void AddError_ShouldAddErrorToParseResult()
        {
            var parser        = A.Fake <Parser>();
            var commandParser = A.Fake <CommandParser <Command1Options> >(ob => ob.WithArgumentsForConstructor(() => new CommandParser <Command1Options>(parser)));
            var parseResult   = A.Fake <ParseResult>();

            var context = new CommandValidatorContext <Command1Options>(commandParser, parseResult);

            var missingCommandError = new MissingCommandError();

            context.AddError(missingCommandError);

            A.CallTo(() => parseResult.AddError(missingCommandError)).MustHaveHappened();
        }
        public ParserResult Parse(string line)
        {
            var tokens = Tokenizer.Tokenize(line).Select(token => token?.Trim()).Where(token => token != null).ToList();

            if (tokens.Count < 1)
            {
                return(new ParserError(
                           Settings.Localization.Errors.NoInput,
                           new ArgumentException(
                               @"No argument values were provided so unable to find a command.", nameof(tokens)
                               )
                           ).AsResult());
            }

            var commandName = tokens[0];
            var command     = Find(commandName ?? throw new InvalidOperationException());

            if (command == null)
            {
                return(MissingCommandError.Create(commandName, Settings.Localization.Errors.CommandNotFound).AsResult());
            }

            var positionalArguments = 0;
            var parsed = new Dictionary <ICommandArgument, ArgumentValues>();
            var errors = new List <ParserError>();

            for (var tokenIndex = 1; tokenIndex < tokens.Count; ++tokenIndex)
            {
                var token = tokens[tokenIndex];

                if (token == null)
                {
                    throw new InvalidOperationException(@"None of the cleaned arguments should be null at this point.");
                }

                var canBeShortName = token.StartsWith(Settings.PrefixShort);
                if (canBeShortName)
                {
                    var expectedLength = Settings.PrefixShort.Length + 1;
                    var actualLength   = token.Contains('=') ? token.IndexOf('=') : token.Length;
                    canBeShortName = expectedLength == actualLength;
                }

                var canBeLongName = token.StartsWith(Settings.PrefixLong);
                if (canBeLongName)
                {
                    var actualLength         = token.Length;
                    var maximumInvalidLength = Settings.PrefixLong.Length + (token.Contains('=') ? 2 : 1);
                    canBeLongName = actualLength > maximumInvalidLength;
                }

                var isPositional = false;
                if (!canBeShortName && !canBeLongName)
                {
                    if (positionalArguments < command.PositionalArguments.Count)
                    {
                        isPositional = true;
                    }
                    else
                    {
                        errors.Add(new ParserError(Settings.Localization.Errors.BadArgumentFormat.ToString(token)));

                        continue;
                    }
                }

                if (canBeShortName && canBeLongName)
                {
                    errors.Add(
                        new ParserError(
                            Settings.Localization.Errors.IllegalArgumentFormat.ToString(
                                token, Settings.PrefixShort, Settings.PrefixLong
                                )
                            )
                        );

                    continue;
                }

                var cleanArgParts = token.Split('=');
                var cleanArgName  = cleanArgParts[0] ?? "";
                var cleanArgValue = (cleanArgParts.Length == 2 ? cleanArgParts[1] : null) ?? "";

                if (isPositional)
                {
                    cleanArgValue = cleanArgName;
                }

                var argument = isPositional ? command.PositionalArguments[positionalArguments] :
                               canBeShortName?command.FindArgument(cleanArgName[Settings.PrefixShort.Length]) :
                                   command.FindArgument(cleanArgName.Substring(Settings.PrefixLong.Length));

                if (argument == null)
                {
                    if (isPositional)
                    {
                        errors.Add(
                            UnhandledArgumentError.Create(
                                command.Name, positionalArguments, cleanArgValue,
                                Settings.Localization.Errors.UnhandledPositionalArgument
                                )
                            );
                    }
                    else
                    {
                        errors.Add(
                            UnhandledArgumentError.Create(
                                command.Name, cleanArgName, Settings.Localization.Errors.UnhandledNamedArgument
                                )
                            );
                    }

                    continue;
                }

                var argumentDisplayName = isPositional ? argument.Name : cleanArgName;

                var typeName = argument.ValueType.Name;
                if (Settings.Localization.TypeNames.TryGetValue(typeName, out var localizedType))
                {
                    typeName = localizedType;
                }

                List <object> values;
                if (parsed.TryGetValue(argument, out var argumentValues))
                {
                    if (!argument.AllowsMultiple)
                    {
                        errors.Add(
                            new ParserError(
                                Settings.Localization.Errors.DuplicateNamedArgument.ToString(argumentDisplayName), false
                                )
                            );

                        continue;
                    }

                    values = argumentValues.Values.ToList();
                }
                else
                {
                    values = new List <object>();
                }

                if (argument.IsFlag)
                {
                    if (!string.IsNullOrEmpty(cleanArgValue))
                    {
                        errors.Add(
                            new ParserError(
                                Settings.Localization.Errors.FlagArgumentsIgnoreValue.ToString(argumentDisplayName),
                                false
                                )
                            );
                    }

                    values.Add(true);
                }
                else if (argument.IsCollection)
                {
                    if (argument.Delimeter == null)
                    {
                    }
                    else
                    {
                        var defaultValue     = argument.ValueTypeDefault;
                        var parsedPartValues = cleanArgValue.Split(new[] { argument.Delimeter }, StringSplitOptions.None)
                                               .Select(
                            valuePart =>
                        {
                            if (string.IsNullOrEmpty(valuePart))
                            {
                                return(defaultValue);
                            }

                            if (TryParseArgument(
                                    argument.ValueType, defaultValue, valuePart, out var parsedPart
                                    ))
                            {
                                if (!argument.IsValueAllowed(parsedPart))
                                {
                                    errors.Add(
                                        new ParserError(
                                            Settings.Localization.Errors.InvalidArgumentValue.ToString(
                                                valuePart, argumentDisplayName
                                                )
                                            )
                                        );
                                }

                                return(parsedPart);
                            }

                            if (argument.ValueType != typeof(object))
                            {
                                errors.Add(
                                    new ParserError(
                                        Settings.Localization.Errors.InvalidArgumentValueWithType.ToString(
                                            valuePart, argumentDisplayName, typeName
                                            ), false
                                        )
                                    );
                            }
                            else
                            {
                                errors.Add(
                                    new ParserError(
                                        Settings.Localization.Errors.InvalidArgumentValue.ToString(
                                            valuePart, argumentDisplayName
                                            ), false
                                        )
                                    );
                            }

                            return(defaultValue);
                        }
                            );

                        values.AddRange(parsedPartValues);
                    }
                }
                else
                {
                    if (!TryParseArgument(argument.ValueType, argument.DefaultValue, cleanArgValue, out var value))
                    {
                        if (argument.ValueType != typeof(object))
                        {
                            errors.Add(
                                new ParserError(
                                    Settings.Localization.Errors.InvalidArgumentValueWithType.ToString(
                                        cleanArgValue, argumentDisplayName, typeName
                                        ), true
                                    )
                                );
                        }
                        else
                        {
                            errors.Add(
                                new ParserError(
                                    Settings.Localization.Errors.InvalidArgumentValue.ToString(
                                        cleanArgValue, argumentDisplayName
                                        ), false
                                    )
                                );
                        }
                    }

                    if (!argument.IsValueAllowed(value))
                    {
                        errors.Add(
                            new ParserError(

                                // TODO: DisallowedArgumentValue message instead
                                Settings.Localization.Errors.InvalidArgumentValue.ToString(value, argumentDisplayName)
                                )
                            );
                    }

                    values.Add(value);
                }

                parsed[argument] = new ArgumentValues(argumentDisplayName, values);

                if (isPositional)
                {
                    ++positionalArguments;
                }
            }

            var parserContext = new ParserContext
            {
                Command = command,
                Tokens  = tokens.ToImmutableList(),
                Errors  = errors.ToImmutableList(),
                Parsed  = parsed.ToImmutableDictionary()
            };

            var omitted = new List <ICommandArgument>();
            var missing = new List <ICommandArgument>();

            foreach (var argument in command.Arguments)
            {
                if (!argument.IsRequired(parserContext) || parsed.ContainsKey(argument))
                {
                    if (!parsed.ContainsKey(argument))
                    {
                        parsed[argument] = ConstructDefaultArgument(argument, true);
                        omitted.Add(argument);
                    }

                    continue;
                }

                if (argument.IsPositional)
                {
                    errors.Add(
                        MissingArgumentError.Create(
                            Settings.Localization.Errors.MissingPositionalArgument, argument.Name, commandName
                            )
                        );
                }
                else
                {
                    errors.Add(MissingArgumentError.Create(Settings.PrefixLong, argument.Name, commandName));
                }

                missing.Add(argument);
            }

            return(new ParserResult(command, new ArgumentValuesMap(parsed), errors, missing, omitted));
        }