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>(); }
/// <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)); }
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(); }
public void DefaultedReader() { var context = new ArgumentParseContext(); Action setNull = () => context.FileSystemReader = null; setNull.Should().NotThrow <ArgumentNullException>(); context.FileSystemReader.Should().NotBeNull(); }
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); }
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); } }
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(); }
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(); }
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)); }
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)); }