private static ArgumentDefinition CreateArgumentDescriptor( IMutableMemberInfo member, ArgumentBaseAttribute attribute, object defaultValues, ArgumentSetDefinition argSet, object fixedDestination, ArgumentDefinition containingArgument, ServiceConfigurer serviceConfigurer) { if (!member.IsReadable || !member.IsWritable) { var declaringType = member.MemberInfo.DeclaringType; throw new InvalidArgumentSetException(member, string.Format( CultureInfo.CurrentCulture, Strings.MemberNotSupported, member.MemberInfo.Name, declaringType?.Name)); } var defaultFieldValue = (defaultValues != null) ? member.GetValue(defaultValues) : null; return(new ArgumentDefinition(member, attribute, argSet, defaultValue: defaultFieldValue, fixedDestination: fixedDestination, containingArgument: containingArgument, serviceConfigurer: serviceConfigurer)); }
private bool TryParseAndStore(ArgumentDefinition arg, string value, object dest) { if (!arg.TryParseAndStore(value, dest, out object parsedValue)) { return(false); } // Inspect the parsed value. var argProvider = parsedValue as IArgumentProvider; if (argProvider == null) { argProvider = (arg.FixedDestination ?? dest) as IArgumentProvider; } if (argProvider != null) { var definingType = argProvider.GetTypeDefiningArguments(); if (definingType != null) { _argumentSet.AddArgumentsFromTypeWithAttributes(definingType, fixedDestination: argProvider.GetDestinationObject()); } } return(true); }
/// <summary> /// Constructs a result for cases where parser is ready. /// </summary> /// <param name="lastParsedArg">If non-null, indicates the last argument /// that was parsed.</param> /// <returns>The result object. </returns> public static ArgumentSetParseResult Ready(ArgumentDefinition lastParsedArg) { return(new ArgumentSetParseResult(ArgumentSetParseResultType.Ready) { LastSeenArg = lastParsedArg }); }
/// <summary> /// Checks if this parser has seen a value provided for the given argument. /// </summary> /// <param name="arg">The argument to look for.</param> /// <returns>True if a value has been seen; false otherwise.</returns> public bool HasSeenValueFor(ArgumentDefinition arg) { if (!_stateByArg.TryGetValue(arg, out ArgumentParser parser)) { return(false); } return(parser.SeenValue); }
/// <summary> /// Registers an Argument that conflicts with the one described by this /// object. If the specified argument has already previously been /// registered, then this operation is a no-op. /// </summary> /// <param name="arg">The conflicting argument.</param> public void AddConflictingArgument(ArgumentDefinition arg) { if (arg == this) { throw new ArgumentOutOfRangeException(nameof(arg)); } _conflictingArgs.Add(arg); }
public ArgumentDefinition(MemberInfo member, ArgumentBaseAttribute attribute, ArgumentSetDefinition argSet, object defaultValue = null, ArgumentDefinition containingArgument = null, ServiceConfigurer serviceConfigurer = null) : this(GetMutableMemberInfo(member), attribute, argSet, defaultValue, /*fixedDestination=*/ null, containingArgument, serviceConfigurer) { }
/// <summary> /// Constructs a result for cases where parser is next looking to see an argument /// to an option. /// </summary> /// <param name="arg">Argument that requires option argument.</param> /// <returns>The constructed result object.</returns> public static ArgumentSetParseResult RequiresOptionArgument(ArgumentDefinition arg) { if (arg == null) { throw new ArgumentNullException(nameof(arg)); } return(new ArgumentSetParseResult(ArgumentSetParseResultType.RequiresOptionArgument) { LastSeenArg = arg }); }
public static TokenParseResult RequiresOptionArgument(ArgumentDefinition arg) { if (arg == null) { throw new ArgumentNullException(nameof(arg)); } return(new TokenParseResult(TokenParseResultState.RequiresOptionArgument) { Argument = arg }); }
/// <summary> /// Constructor that forms the info from the argument's metadata. /// </summary> /// <param name="arg">Argument metadata.</param> /// <param name="currentValue">Current value.</param> public ArgumentUsageInfo(ArgumentDefinition arg, object currentValue = null) : this( syntax : arg.GetSyntaxSummary(detailed : false), detailedSyntax : arg.GetSyntaxSummary(detailed : true), description : arg.Attribute.Description, required : arg.IsRequired, shortName : arg.ShortName, defaultValue : TryGetDefaultValueString(arg, onlyReturnExplicitDefaults : true), argType : arg.ArgumentType, currentValue : currentValue) { }
/// <summary> /// Tries to find the argument's default value. /// </summary> /// <param name="arg">The argument to retrieve a default value from.</param> /// <param name="onlyReturnExplicitDefaults">True to only return /// a default if it was explicitly specified; false to report on /// the default, even if it was defaulted itself.</param> /// <param name="value">On success, receives the default value /// for this argument; otherwise, receives null.</param> /// <returns>True on success, false otherwise.</returns> public static bool TryGetDefaultValue(ArgumentDefinition arg, bool onlyReturnExplicitDefaults, out object value) { // Firstly, if the argument is required, then there's no need to // indicate any default value. if (arg.IsRequired) { value = null; return(false); } // Next, go check for the actual *effective* default value for the // argument; we may still receive back a value here even if one // wasn't explicitly declared, as we will have consulted with the // argument's type to determine its default value. if (onlyReturnExplicitDefaults && !arg.HasDefaultValue) { value = null; return(false); } // If the default value is null, then that's not useful to show the // user. var defaultValue = arg.EffectiveDefaultValue; if (defaultValue == null) { value = null; return(false); } // Special case: if the argument type is bool, then the argument // will be like a switch, and that's typically assumed to be false // if not present. So if the default value is indeed 'false', then // don't bother displaying it; but if it's 'true', then it's // important to indicate that. if ((defaultValue is bool) && !((bool)defaultValue)) { value = null; return(false); } // Special case: if the argument type is string, then it's safe // to assume that its default value is an empty string. if ((defaultValue is string stringDefaultValue) && string.IsNullOrEmpty(stringDefaultValue)) { value = null; return(false); } // Okay, we have the value. value = defaultValue; return(true); }
/// <summary> /// Internal constructor. /// </summary> /// <param name="member">Field to describe.</param> /// <param name="attribute">Argument attribute on the field.</param> /// <param name="argSet">Argument set containing this argument.</param> /// <param name="defaultValue">Default value for the field.</param> /// <param name="fixedDestination">Optionally provides fixed parse destination object.</param> /// <param name="containingArgument">Optionally provides a reference /// to the definition of the argument that "contains" these arguments. /// </param> /// <param name="serviceConfigurer">Optionally provides a service configurer.</param> internal ArgumentDefinition(IMutableMemberInfo member, ArgumentBaseAttribute attribute, ArgumentSetDefinition argSet, object defaultValue = null, object fixedDestination = null, ArgumentDefinition containingArgument = null, ServiceConfigurer serviceConfigurer = null) { Member = member ?? throw new ArgumentNullException(nameof(member)); Attribute = attribute ?? throw new ArgumentNullException(nameof(attribute)); ContainingSet = argSet ?? throw new ArgumentNullException(nameof(argSet)); ContainingArgument = containingArgument; FixedDestination = fixedDestination; IsPositional = attribute is PositionalArgumentAttribute; ArgumentType = GetArgumentType(Attribute, member, member.MemberType, serviceConfigurer); CollectionArgumentType = AsCollectionType(ArgumentType); HasDefaultValue = attribute.ExplicitDefaultValue || attribute.DynamicDefaultValue; ValidationAttributes = GetValidationAttributes(ArgumentType, Member); LongName = GetLongName(attribute, argSet.Attribute, member.MemberInfo); ExplicitShortName = HasExplicitShortName(attribute); ShortName = GetShortNameOrNull(attribute, argSet.Attribute, member.MemberInfo); DefaultValue = GetDefaultValue(attribute, member, defaultValue); var nullableBase = Nullable.GetUnderlyingType(member.MemberType); if (CollectionArgumentType != null) { ValueType = CollectionArgumentType.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 = GetArgumentType(Attribute, member, nullableBase, serviceConfigurer); } else { ValueType = ArgumentType; } Debug.Assert(ValueType != null); if (Unique && !IsCollection) { throw new InvalidArgumentSetException(member, Strings.UniqueUsedOnNonCollectionArgument); } Debug.Assert(!string.IsNullOrEmpty(LongName)); }
/// <summary> /// Constructs a new stateful parser for the given argument. /// </summary> /// <param name="argSet">The argument set containing the argument to be parsed.</param> /// <param name="arg">The definition of the argument.</param> /// <param name="options">General options for parsing this argument set.</param> /// <param name="destination">The destination object into which the parsed result should go, /// if so desired; null otherwise to parse without saving results.</param> public ArgumentParser(ArgumentSetDefinition argSet, ArgumentDefinition arg, CommandLineParserOptions options, object destination = null) { ArgumentSet = argSet ?? throw new ArgumentNullException(nameof(argSet)); Argument = arg ?? throw new ArgumentNullException(nameof(arg)); Reporter = options?.Reporter ?? (s => { }); DestinationObject = arg.FixedDestination ?? destination; ParseContext = CreateParseContext(Argument, ArgumentSet.Attribute, options, DestinationObject); if (Argument.IsCollection) { CollectionValues = GenericCollectionFactory.CreateList(Argument.CollectionArgumentType.ElementType.Type); } }
/// <summary> /// Tries to construct a string describing the argument's default value. /// </summary> /// <param name="arg">The argument to retrieve a default value string /// from.</param> /// <param name="onlyReturnExplicitDefaults">True to only return /// a default if it was explicitly specified; false to report on /// the default, even if it was defaulted itself.</param> /// <returns>If one should be advertised, returns the string version of /// the default value for this argument; otherwise, returns null. /// </returns> public static string TryGetDefaultValueString(ArgumentDefinition arg, bool onlyReturnExplicitDefaults = false) { // Try to get the default value. if (!TryGetDefaultValue(arg, onlyReturnExplicitDefaults, out object defaultValue)) { return(null); } // Now turn the value into a string. var formattedArg = arg.Format(defaultValue, suppressArgNames: true).ToList(); if (formattedArg.Count == 0) { return(null); } return(string.Join(" ", formattedArg)); }
private void AddPositionalArgument(ArgumentDefinition arg, int positionalIndexBias) { var attrib = (PositionalArgumentAttribute)arg.Attribute; var position = positionalIndexBias + attrib.Position; if (_positionalArguments.ContainsKey(position)) { throw new InvalidArgumentSetException(arg, string.Format( CultureInfo.CurrentCulture, Strings.DuplicatePositionArguments, _positionalArguments[position].Member.MemberInfo.Name, arg.Member.MemberInfo.Name, position)); } _positionalArguments.Add(position, arg); _nextPositionalArgIndexToDefine = position + 1; }
/// <summary> /// Retrieves the parse state for the given argument. If no such state exists, /// then a state object is constructed and persisted. /// </summary> /// <param name="arg">The argument to look up.</param> /// <param name="destination">Optionally provides the destination object /// being parsed into.</param> /// <returns>The parse state for the given argument.</returns> private ArgumentParser GetStateForArgument(ArgumentDefinition arg, object destination) { ArgumentParser parser; if (_stateByArg.ContainsKey(arg)) { parser = _stateByArg[arg]; if (destination != null && parser.Argument.FixedDestination == null && parser.DestinationObject != destination) { throw new InvalidOperationException(); } } else { parser = _stateByArg[arg] = new ArgumentParser(ArgumentSet, arg, _options, destination); } return(parser); }
public static void AddToArgumentSet( ArgumentSetDefinition argSet, Type typeToReflectOn, object defaultValues = null, object fixedDestination = null, ArgumentDefinition containingArgument = null, ServiceConfigurer serviceConfigurer = null) { // Extract argument descriptors from the defining type. var args = GetArgumentDescriptors( typeToReflectOn, argSet, defaultValues, fixedDestination, containingArgument, serviceConfigurer).ToList(); // Define the arguments. argSet.Add(args); // If the provided type we're reflecting on has an ArgumentSetAttribute, // then add that as auxiliary information. var auxiliaryAttrib = TryGetSetAttribute(typeToReflectOn); if (auxiliaryAttrib != null) { argSet.AddAuxiliaryAttribute(auxiliaryAttrib); } // If the argument set doesn't already have a default assembly associated // with it, then fill that out. if (argSet.DefaultAssembly == null) { argSet.DefaultAssembly = typeToReflectOn.GetTypeInfo().Assembly; } }
/// <summary> /// Try to look up a positional argument by position. /// </summary> /// <param name="position">0-based position index to look up.</param> /// <param name="arg">On success, receives the argument.</param> /// <returns>True on success; false otherwise.</returns> public bool TryGetPositionalArgument(int position, out ArgumentDefinition arg) => _positionalArguments.TryGetValue(position, out arg);
/// <summary> /// Try to look up a named argument by short or long name. /// </summary> /// <param name="nameType">Type of name to look up.</param> /// <param name="name">Name to look up.</param> /// <param name="arg">On success, receives the named argument.</param> /// <returns>True on success; false otherwise.</returns> public bool TryGetNamedArgument(ArgumentNameType nameType, string name, out ArgumentDefinition arg) { var dict = GetNamedArgumentDictionary(nameType); return(dict.TryGetValue(name, out arg)); }
/// <summary> /// Adds an argument. /// </summary> /// <param name="arg">Argument to define.</param> public void AddArgument(ArgumentDefinition arg) => Add(new[] { arg });
private void AddNamedArgument(ArgumentDefinition argument) { // // Validate and register the long name. // if (_namedArgumentsByName.ContainsKey(argument.LongName)) { throw new InvalidArgumentSetException(argument, string.Format( CultureInfo.CurrentCulture, Strings.DuplicateArgumentLongName, argument.LongName)); } _namedArgumentsByName.Add(argument.LongName, argument); // // Validate and register the short name. // if (!string.IsNullOrEmpty(argument.ShortName)) { if (_namedArgumentsByName.TryGetValue(argument.ShortName, out ArgumentDefinition conflictingArg)) { Debug.Assert(conflictingArg != null); if (argument.ExplicitShortName) { if (conflictingArg.ExplicitShortName) { throw new InvalidArgumentSetException(argument, string.Format(CultureInfo.CurrentCulture, Strings.DuplicateArgumentShortName, argument.ShortName)); } else { // TODO: Decide whether this works for dynamically imported args. _namedArgumentsByName.Remove(conflictingArg.ShortName); conflictingArg.ClearShortName(); } } else { argument.ClearShortName(); } } } if (!string.IsNullOrEmpty(argument.ShortName)) { if (Attribute.AllowMultipleShortNamesInOneToken && argument.ShortName.Length > 1) { throw new InvalidArgumentSetException(argument, string.Format(CultureInfo.CurrentCulture, Strings.ArgumentShortNameTooLong, argument.ShortName)); } _namedArgumentsByName.Add(argument.ShortName, argument); } // Add to unique list. _namedArguments.Add(argument); }
private static IEnumerable <ArgumentDefinition> CreateArgumentDescriptorsIfApplicable(IMutableMemberInfo member, object defaultValues, ArgumentSetDefinition argSet, object fixedDestination, ArgumentDefinition containingArgument, ServiceConfigurer serviceConfigurer) { var descriptors = Enumerable.Empty <ArgumentDefinition>(); var argAttrib = member.MemberInfo.GetSingleAttribute <ArgumentBaseAttribute>(); if (argAttrib != null) { descriptors = descriptors.Concat( new[] { CreateArgumentDescriptor( member, argAttrib, defaultValues, argSet, fixedDestination, containingArgument, serviceConfigurer) }); } return(descriptors); }
private static IEnumerable <ArgumentDefinition> GetArgumentDescriptors(Type type, ArgumentSetDefinition argSet, object defaultValues, object fixedDestination, ArgumentDefinition containingArgument, ServiceConfigurer serviceConfigurer) { // Find all fields and properties that have argument attributes on // them. For each that we find, capture information about them. var argList = GetAllFieldsAndProperties(type, includeNonPublicMembers: true) .SelectMany(member => CreateArgumentDescriptorsIfApplicable(member, defaultValues, argSet, fixedDestination, containingArgument, serviceConfigurer)); // If the argument set attribute indicates that we should also // include un-attributed, public, writable members as named // arguments, then look for them now. if (argSet.Attribute.PublicMembersAreNamedArguments) { argList = argList.Concat(GetAllFieldsAndProperties(type, includeNonPublicMembers: false) .Where(member => member.IsWritable) .Where(member => member.MemberInfo.GetSingleAttribute <ArgumentBaseAttribute>() == null) .Select(member => CreateArgumentDescriptor( member, new NamedArgumentAttribute(), defaultValues, argSet, fixedDestination, containingArgument, serviceConfigurer))); } return(argList); }
/// <summary> /// Try to look up a named argument by name (short name or long name). /// </summary> /// <param name="name">Name to look up.</param> /// <param name="arg">On success, receives the named argument.</param> /// <returns>True on success; false otherwise.</returns> public bool TryGetNamedArgument(string name, out ArgumentDefinition arg) => _namedArgumentsByName.TryGetValue(name, out arg);