Exemple #1
0
        public void ParsingNonBinaryUnits()
        {
            var defaultContext = ArgumentParseContext.Default;
            var context        = new ArgumentParseContext {
                NumberOptions = NumberOptions.AllowMetricUnitSuffix
            };


            ArgumentType.Int.TryParse(defaultContext, "3k", out object value).Should().BeFalse();
            ArgumentType.Int.TryParse(context, "3kB", out value).Should().BeFalse();
            ArgumentType.Int.TryParse(context, "3MB", out value).Should().BeFalse();
            ArgumentType.Int.TryParse(context, "3T", out value).Should().BeFalse();
            ArgumentType.Int.TryParse(context, "3DA", out value).Should().BeFalse();
            ArgumentType.Int.TryParse(context, "3H", out value).Should().BeFalse();

            ArgumentType.Int.TryParse(context, "3", out value).Should().BeTrue();
            value.Should().Be(3);

            ArgumentType.Int.TryParse(context, "3da", out value).Should().BeTrue();
            value.Should().Be(30);

            ArgumentType.Int.TryParse(context, "3h", out value).Should().BeTrue();
            value.Should().Be(300);

            ArgumentType.Int.TryParse(context, "3k", out value).Should().BeTrue();
            value.Should().Be(3000);

            ArgumentType.Int.TryParse(context, "3M", out value).Should().BeTrue();
            value.Should().Be(3000000);
        }
        public void InvalidReader()
        {
            var context = new ArgumentParseContext();

            Action setNull = () => context.FileSystemReader = null;

            setNull.ShouldThrow <ArgumentNullException>();
        }
Exemple #3
0
        /// <summary>
        /// Primary constructor.
        /// </summary>
        /// <param name="member">Field to describe.</param>
        /// <param name="attribute">Argument attribute on the field.</param>
        /// <param name="setAttribute">Attribute on the containing argument set.</param>
        /// <param name="options">Provides parser options.</param>
        /// <param name="defaultFieldValue">Default value for the field.</param>
        /// <param name="parentMember">Parent member under which this field sits.</param>
        /// <param name="fixedDestination">Optionally provides fixed parse destination object.</param>
        public ArgumentDefinition(IMutableMemberInfo member,
                                  ArgumentBaseAttribute attribute,
                                  ArgumentSetAttribute setAttribute,
                                  CommandLineParserOptions options,
                                  object defaultFieldValue        = null,
                                  IMutableMemberInfo parentMember = null,
                                  object fixedDestination         = null)
        {
            Debug.Assert(attribute != null);
            Debug.Assert(member != null);

            Member                = member;
            ParentMember          = parentMember;
            Attribute             = attribute;
            _setAttribute         = setAttribute;
            FixedDestination      = fixedDestination;
            _isPositional         = attribute is PositionalArgumentAttribute;
            _reporter             = options?.Reporter ?? (s => { });
            ArgumentType          = Attribute.GetArgumentType(member.MemberType);
            _collectionArgType    = AsCollectionType(ArgumentType);
            HasDefaultValue       = attribute.ExplicitDefaultValue || attribute.DynamicDefaultValue;
            _validationAttributes = GetValidationAttributes(ArgumentType, Member);
            _argumentParseContext = CreateParseContext(attribute, _setAttribute, options);

            LongName          = GetLongName(attribute, setAttribute, member.MemberInfo);
            ExplicitShortName = HasExplicitShortName(attribute);
            ShortName         = GetShortName(attribute, setAttribute, member.MemberInfo);
            DefaultValue      = GetDefaultValue(attribute, member, defaultFieldValue);

            var nullableBase = Nullable.GetUnderlyingType(member.MemberType);

            if (_collectionArgType != null)
            {
                _collectionValues = new ArrayList();
                _valueType        = _collectionArgType.ElementType;
            }
            else if (nullableBase != null)
            {
                // For nullable arguments, we use the wrapped type (T in
                // Nullable<T>) as the value type. Parsing an enum or int is the
                // same as parsing an enum? or int?, for example, since null can
                // only arise if the value was not provided at all.
                _valueType = Attribute.GetArgumentType(nullableBase);
            }
            else
            {
                _valueType = ArgumentType;
            }

            Debug.Assert(_valueType != null);

            if (Unique && !IsCollection)
            {
                throw new InvalidArgumentSetException(member, Strings.UniqueUsedOnNonCollectionArgument);
            }

            Debug.Assert(!string.IsNullOrEmpty(LongName));
        }
Exemple #4
0
        public void ParsingUnitsWithOverflow()
        {
            var context = new ArgumentParseContext {
                NumberOptions = NumberOptions.AllowBinaryMetricUnitSuffix
            };

            ArgumentType.Int.TryParse(context, "10GB", out object value).Should().BeFalse();
            ArgumentType.Int.TryParse(context, "10TB", out value).Should().BeFalse();
        }
Exemple #5
0
        public void DefaultedReader()
        {
            var context = new ArgumentParseContext();

            Action setNull = () => context.FileSystemReader = null;

            setNull.Should().NotThrow <ArgumentNullException>();

            context.FileSystemReader.Should().NotBeNull();
        }
Exemple #6
0
            public bool TryParse(ArgumentParseContext context, string stringToParse, out object value)
            {
                if (!stringToParse.StartsWith("|") || !stringToParse.EndsWith("|") || stringToParse.Length < 2)
                {
                    value = null;
                    return(false);
                }

                value = stringToParse.Substring(1, stringToParse.Length - 2);
                return(true);
            }
Exemple #7
0
            public override bool TryParse(ArgumentParseContext context, string stringToParse, out object value)
            {
                switch (stringToParse.ToLowerInvariant())
                {
                case "one":
                    value = One;
                    return(true);

                case "two":
                    value = Two;
                    return(true);

                default:
                    value = null;
                    return(false);
                }
            }
Exemple #8
0
        public void ParsingBinaryUnits()
        {
            var defaultContext = ArgumentParseContext.Default;
            var context        = new ArgumentParseContext {
                NumberOptions = NumberOptions.AllowBinaryMetricUnitSuffix
            };


            ArgumentType.Int.TryParse(defaultContext, "3k", out object value).Should().BeFalse();
            ArgumentType.Int.TryParse(context, "3b", out value).Should().BeFalse();
            ArgumentType.Int.TryParse(context, "3ib", out value).Should().BeFalse();
            ArgumentType.Int.TryParse(context, "3B", out value).Should().BeFalse();
            ArgumentType.Int.TryParse(context, "k", out value).Should().BeFalse();
            ArgumentType.Int.TryParse(context, "M", out value).Should().BeFalse();

            ArgumentType.Int.TryParse(context, "3", out value).Should().BeTrue();
            value.Should().Be(3);

            foreach (var s in new[] { "3k", "3KB", "3KiB", "3kB" })
            {
                ArgumentType.Int.TryParse(context, s, out value).Should().BeTrue();
                value.Should().Be(3 * 1024);
            }

            foreach (var s in new[] { "3M", "3MB", "3MiB" })
            {
                ArgumentType.Int.TryParse(context, s, out value).Should().BeTrue();
                value.Should().Be(3 * 1024 * 1024);
            }

            ArgumentType.Int.TryParse(context, "1G", out value).Should().BeTrue();
            value.Should().Be(1024 * 1024 * 1024);

            ArgumentType.Long.TryParse(context, "2T", out value).Should().BeTrue();
            value.Should().Be(2L * 1024 * 1024 * 1024 * 1024);

            ArgumentType.Long.TryParse(context, "2P", out value).Should().BeTrue();
            value.Should().Be(2L * 1024 * 1024 * 1024 * 1024 * 1024);

            ArgumentType.Long.TryParse(context, "2E", out value).Should().BeTrue();
            value.Should().Be(2L * 1024 * 1024 * 1024 * 1024 * 1024 * 1024);

            ArgumentType.Long.TryParse(context, "2Z", out value).Should().BeFalse();
            ArgumentType.Long.TryParse(context, "2Y", out value).Should().BeFalse();
        }
Exemple #9
0
        public void ParsingDecimalUnits()
        {
            var context = new ArgumentParseContext {
                NumberOptions = NumberOptions.AllowMetricUnitSuffix
            };

            ArgumentType.Int.TryParse(context, "1.1234k", out object value).Should().BeFalse();

            ArgumentType.Int.TryParse(context, "1.5k", out value).Should().BeTrue();
            value.Should().Be(1500);

            ArgumentType.Int.TryParse(context, "1.333k", out value).Should().BeTrue();
            value.Should().Be(1333);

            ArgumentType.Int.TryParse(context, "-1.5k", out value).Should().BeTrue();
            value.Should().Be(-1500);

            ArgumentType.Int.TryParse(context, "1.5k", out value).Should().BeTrue();
            value.Should().Be(1500);

            ArgumentType.UInt.TryParse(context, "-1.5k", out value).Should().BeFalse();
        }
Exemple #10
0
 public override bool TryParse(ArgumentParseContext context, string stringToParse, out object value)
 {
     throw new NotImplementedException();
 }
        /// <summary>
        /// Generate possible completions for the specified token in the
        /// provided set of input tokens.
        /// </summary>
        /// <param name="tokens">The tokens.</param>
        /// <param name="indexOfTokenToComplete">Index of the token to complete.
        /// </param>
        /// <param name="destObjectFactory">If non-null, provides a factory
        /// function that can be used to create an object suitable to being
        /// filled out by this parser instance.</param>
        /// <returns>The candidate completions for the specified token.
        /// </returns>
        public IEnumerable <string> GetCompletions(IEnumerable <string> tokens, int indexOfTokenToComplete, Func <object> destObjectFactory)
        {
            Func <IEnumerable <string> > emptyCompletions = Enumerable.Empty <string>;

            var tokenList = tokens.ToList();

            // Complain bitterly if we were asked to complete something far beyond
            // the token list we received.
            if (indexOfTokenToComplete > tokenList.Count)
            {
                throw new ArgumentOutOfRangeException(nameof(indexOfTokenToComplete));
            }

            // If we were asked to complete the token just after the list we received,
            // then this means we're generating completions to append to the full
            // token list.  Insert an empty token and use that as the basis for
            // completion.
            if (indexOfTokenToComplete == tokenList.Count)
            {
                tokenList = tokenList.Concat(new[] { string.Empty }).ToList();
            }

            // Figure out the token we're going to complete.
            var tokenToComplete = tokenList[indexOfTokenToComplete];

            // Create a destination object if provided with a factory.
            var inProgressParsedObject = destObjectFactory?.Invoke();

            // Parse what we've seen thus far (before the token /to complete).
            // Note that this parse attempt may fail; we ignore success/failure.
            var tokensToParse = tokenList.Take(indexOfTokenToComplete).ToList();
            var parseResult   = ParseArgumentList(tokensToParse, inProgressParsedObject);

            // See if we're expecting this token to be an option argument; if
            // so, then we can generate completions based on that.
            if (parseResult.State == TokenParseResultState.RequiresOptionArgument &&
                _argumentSet.Attribute.AllowNamedArgumentValueAsSucceedingToken)
            {
                return(parseResult.Argument.GetCompletions(
                           tokenList,
                           indexOfTokenToComplete,
                           tokenToComplete,
                           inProgressParsedObject));
            }

            // See if the token to complete appears to be a (long-)named argument.
            var longNameArgumentPrefix = TryGetLongNameArgumentPrefix(tokenToComplete);

            if (longNameArgumentPrefix != null)
            {
                var afterPrefix = tokenToComplete.Substring(longNameArgumentPrefix.Length);
                return(GetNamedArgumentCompletions(tokenList, indexOfTokenToComplete, afterPrefix, inProgressParsedObject)
                       .Select(completion => longNameArgumentPrefix + completion));
            }

            // See if the token to complete appears to be a (short-)named argument.
            var shortNameArgumentPrefix = TryGetShortNameArgumentPrefix(tokenToComplete);

            if (shortNameArgumentPrefix != null)
            {
                var afterPrefix = tokenToComplete.Substring(shortNameArgumentPrefix.Length);
                return(GetNamedArgumentCompletions(tokenList, indexOfTokenToComplete, afterPrefix, inProgressParsedObject)
                       .Select(completion => shortNameArgumentPrefix + completion));
            }

            // See if the token to complete appears to be the special answer-file argument.
            var answerFileArgumentPrefix = TryGetAnswerFilePrefix(tokenToComplete);

            if (answerFileArgumentPrefix != null)
            {
                var filePath     = tokenToComplete.Substring(answerFileArgumentPrefix.Length);
                var parseContext = new ArgumentParseContext
                {
                    FileSystemReader = _options.FileSystemReader,
                    ParserContext    = _options.Context,
                    CaseSensitive    = _argumentSet.Attribute.CaseSensitive
                };

                var completionContext = new ArgumentCompletionContext
                {
                    ParseContext           = parseContext,
                    TokenIndex             = indexOfTokenToComplete,
                    Tokens                 = tokenList,
                    InProgressParsedObject = inProgressParsedObject,
                    CaseSensitive          = _argumentSet.Attribute.CaseSensitive
                };

                return(ArgumentType.FileSystemPath.GetCompletions(completionContext, filePath)
                       .Select(completion => answerFileArgumentPrefix + completion));
            }

            // At this point, assume it must be a positional argument. If it's not, then there's not much
            // that we can do.
            if (!_argumentSet.TryGetPositionalArgument(_nextPositionalArgIndexToParse, out ArgumentDefinition positionalArg))
            {
                return(emptyCompletions());
            }

            return(positionalArg.GetCompletions(
                       tokenList,
                       indexOfTokenToComplete,
                       tokenToComplete,
                       inProgressParsedObject));
        }
Exemple #12
0
 protected override object Parse(ArgumentParseContext context, string stringToParse) =>
 ParseHandler(context, stringToParse);
        /// <summary>
        /// Generates possible completions for the indicated argument token.
        /// </summary>
        /// <param name="tokens">Full list of tokens in context.</param>
        /// <param name="indexOfTokenToComplete">0-based index of token
        /// to complete; must either reference valid token in <paramref name="tokens"/>
        /// or the index of the next token that would follow the provided
        /// tokens.</param>
        /// <param name="destObjectFactory">Optionally provides a function
        /// that may be used to instantiate an object of the destination
        /// parse output type.</param>
        /// <returns>Possible completions for the token.</returns>
        public IEnumerable <string> GetCompletions(IEnumerable <string> tokens, int indexOfTokenToComplete, Func <object> destObjectFactory)
        {
            Func <IEnumerable <string> > emptyCompletions = Enumerable.Empty <string>;

            var tokenList = tokens.ToList();

            // Complain bitterly if we were asked to complete something far beyond
            // the token list we received.
            if (indexOfTokenToComplete > tokenList.Count)
            {
                throw new ArgumentOutOfRangeException(nameof(indexOfTokenToComplete));
            }

            // If we were asked to complete the token just after the list we received,
            // then this means we're generating completions to append to the full
            // token list.  Insert an empty token and use that as the basis for
            // completion.
            if (indexOfTokenToComplete == tokenList.Count)
            {
                tokenList = tokenList.Concat(new[] { string.Empty }).ToList();
            }

            // Figure out the token we're going to complete.
            var tokenToComplete = tokenList[indexOfTokenToComplete];

            // Create a destination object if provided with a factory.
            var inProgressParsedObject = destObjectFactory?.Invoke();

            // Parse what we've seen thus far (before the token /to complete).
            // Note that this parse attempt may fail; we ignore success/failure.
            var tokensToParse = tokenList.Take(indexOfTokenToComplete).ToList();
            var parseResult   = ParseArgumentList(tokensToParse, inProgressParsedObject);

            // See if we're expecting this token to be an option argument; if
            // so, then we can generate completions based on that.
            bool completeViaLastArg = false;

            if (parseResult.State == ArgumentSetParseResultType.RequiresOptionArgument &&
                ArgumentSet.Attribute.AllowNamedArgumentValueAsSucceedingToken)
            {
                completeViaLastArg = true;
            }

            // See if we just finished parsing an argument that takes the rest
            // of the line.
            if (parseResult.LastSeenArg?.TakesRestOfLine ?? false)
            {
                completeViaLastArg = true;
            }

            // If we can complete via the last seen argument, then construct a parser
            // around the argument and generate the completions.
            if (completeViaLastArg)
            {
                var argParseState = new ArgumentParser(ArgumentSet, parseResult.LastSeenArg, _options, /*destination=*/ null);
                return(argParseState.GetCompletions(
                           tokenList,
                           indexOfTokenToComplete,
                           tokenToComplete,
                           inProgressParsedObject));
            }

            // See if the token to complete appears to be a named argument.
            var longNameArgumentPrefix  = TryGetLongNameArgumentPrefix(tokenToComplete, allowIncompleteToken: true);
            var shortNameArgumentPrefix = TryGetShortNameArgumentPrefix(tokenToComplete, allowIncompleteToken: true);

            if (longNameArgumentPrefix != null || shortNameArgumentPrefix != null)
            {
                var completions = Enumerable.Empty <string>();

                if (longNameArgumentPrefix != null)
                {
                    var prefixLen       = Math.Min(longNameArgumentPrefix.Length, tokenToComplete.Length);
                    var afterLongPrefix = tokenToComplete.Substring(prefixLen);
                    completions = completions.Concat(
                        GetNamedArgumentCompletions(ArgumentNameType.LongName, tokenList, indexOfTokenToComplete, afterLongPrefix, inProgressParsedObject)
                        .Select(completion => longNameArgumentPrefix + completion));
                }

                if (shortNameArgumentPrefix != null)
                {
                    var prefixLen        = Math.Min(shortNameArgumentPrefix.Length, tokenToComplete.Length);
                    var afterShortPrefix = tokenToComplete.Substring(prefixLen);
                    completions = completions.Concat(
                        GetNamedArgumentCompletions(ArgumentNameType.ShortName, tokenList, indexOfTokenToComplete, afterShortPrefix, inProgressParsedObject)
                        .Select(completion => shortNameArgumentPrefix + completion));
                }

                return(completions);
            }

            // See if the token to complete appears to be the special answer-file argument.
            var answerFileArgumentPrefix = TryGetAnswerFilePrefix(tokenToComplete);

            if (answerFileArgumentPrefix != null)
            {
                var filePath     = tokenToComplete.Substring(answerFileArgumentPrefix.Length);
                var parseContext = new ArgumentParseContext
                {
                    FileSystemReader  = _options.FileSystemReader,
                    ParserContext     = _options.Context,
                    ServiceConfigurer = _options.ServiceConfigurer,
                    CaseSensitive     = ArgumentSet.Attribute.CaseSensitive
                };

                var completionContext = new ArgumentCompletionContext
                {
                    ParseContext           = parseContext,
                    TokenIndex             = indexOfTokenToComplete,
                    Tokens                 = tokenList,
                    InProgressParsedObject = inProgressParsedObject,
                    CaseSensitive          = ArgumentSet.Attribute.CaseSensitive
                };

                return(ArgumentType.FileSystemPath.GetCompletions(completionContext, filePath)
                       .Select(completion => answerFileArgumentPrefix + completion));
            }

            // At this point, assume it must be a positional argument. If it's not, then there's not much
            // that we can do.
            if (!ArgumentSet.TryGetPositionalArgument(NextPositionalArgIndexToParse, out ArgumentDefinition positionalArg))
            {
                return(emptyCompletions());
            }

            var parseState = new ArgumentParser(ArgumentSet, positionalArg, _options, /*destination=*/ null);

            return(parseState.GetCompletions(
                       tokenList,
                       indexOfTokenToComplete,
                       tokenToComplete,
                       inProgressParsedObject));
        }