From 1c6cfa50141f847725b99ff2ef706ebfbe63a123 Mon Sep 17 00:00:00 2001 From: yakovypg Date: Sun, 15 Feb 2026 18:13:00 +0300 Subject: [PATCH 1/5] Add the ability to specify 'before parse choices' for value options without needing to specify 'choices' --- .../Attributes/EnumValueOptionAttribute.cs | 4 ++-- .../Attributes/MultipleValueOptionAttribute.cs | 4 ++-- .../Attributes/ValueOptionAttribute.cs | 15 ++++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Core/NetArgumentParser/Attributes/EnumValueOptionAttribute.cs b/Core/NetArgumentParser/Attributes/EnumValueOptionAttribute.cs index 37b9dd0..362ec54 100644 --- a/Core/NetArgumentParser/Attributes/EnumValueOptionAttribute.cs +++ b/Core/NetArgumentParser/Attributes/EnumValueOptionAttribute.cs @@ -61,7 +61,6 @@ public EnumValueOptionAttribute( string[]? beforeParseChoices = null) : base( choices, - beforeParseChoices, longName ?? throw new ArgumentNullException(nameof(longName)), shortName ?? throw new ArgumentNullException(nameof(shortName)), description ?? throw new ArgumentNullException(nameof(description)), @@ -70,7 +69,8 @@ public EnumValueOptionAttribute( isHidden, isFinal, ignoreCaseInChoices, - aliases) + aliases, + beforeParseChoices) { UseDefaultChoices = useDefaultChoices; } diff --git a/Core/NetArgumentParser/Attributes/MultipleValueOptionAttribute.cs b/Core/NetArgumentParser/Attributes/MultipleValueOptionAttribute.cs index 08edac7..fa4a08a 100644 --- a/Core/NetArgumentParser/Attributes/MultipleValueOptionAttribute.cs +++ b/Core/NetArgumentParser/Attributes/MultipleValueOptionAttribute.cs @@ -62,7 +62,6 @@ public MultipleValueOptionAttribute( int numberOfItemsToCapture = -1) : base( choices: null, - beforeParseChoices: null, longName ?? throw new ArgumentNullException(nameof(longName)), shortName ?? throw new ArgumentNullException(nameof(shortName)), description ?? throw new ArgumentNullException(nameof(description)), @@ -71,7 +70,8 @@ public MultipleValueOptionAttribute( isHidden, isFinal, ignoreCaseInChoices, - aliases) + aliases, + beforeParseChoices: null) { IgnoreOrderInChoices = ignoreOrderInChoices; ContextCapture = CreateContextCapture(contextCaptureType, numberOfItemsToCapture); diff --git a/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs b/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs index ce3a33b..1f26707 100644 --- a/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs +++ b/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs @@ -30,7 +30,6 @@ public ValueOptionAttribute( string[]? beforeParseChoices = null) : this( choices, - beforeParseChoices, longName ?? throw new ArgumentNullException(nameof(longName)), shortName ?? throw new ArgumentNullException(nameof(shortName)), description ?? throw new ArgumentNullException(nameof(description)), @@ -39,7 +38,8 @@ public ValueOptionAttribute( isHidden, isFinal, ignoreCaseInChoices, - aliases) + aliases, + beforeParseChoices) { DefaultValue = new DefaultOptionValue(defaultValue); } @@ -54,10 +54,10 @@ public ValueOptionAttribute( bool isHidden = false, bool isFinal = false, bool ignoreCaseInChoices = false, - string[]? aliases = null) + string[]? aliases = null, + string[]? beforeParseChoices = null) : this( choices: null, - beforeParseChoices: null, longName ?? throw new ArgumentNullException(nameof(longName)), shortName ?? throw new ArgumentNullException(nameof(shortName)), description ?? throw new ArgumentNullException(nameof(description)), @@ -66,13 +66,13 @@ public ValueOptionAttribute( isHidden, isFinal, ignoreCaseInChoices, - aliases) + aliases, + beforeParseChoices) { } public ValueOptionAttribute( T[]? choices, - string[]? beforeParseChoices, string longName, string shortName = "", string description = "", @@ -81,7 +81,8 @@ public ValueOptionAttribute( bool isHidden = false, bool isFinal = false, bool ignoreCaseInChoices = false, - string[]? aliases = null) + string[]? aliases = null, + string[]? beforeParseChoices = null) : base( longName ?? throw new ArgumentNullException(nameof(longName)), shortName ?? throw new ArgumentNullException(nameof(shortName)), From 4fdc7f512e865f98d100bcaa3a17d0e01926cf87 Mon Sep 17 00:00:00 2001 From: yakovypg Date: Sun, 15 Feb 2026 18:34:36 +0300 Subject: [PATCH 2/5] Add the ability to add 'choices' and 'before parse choices' to value option description using attributes --- .../Attributes/EnumValueOptionAttribute.cs | 26 +++++++++++--- .../Attributes/ValueOptionAttribute.cs | 35 +++++++++++++++---- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/Core/NetArgumentParser/Attributes/EnumValueOptionAttribute.cs b/Core/NetArgumentParser/Attributes/EnumValueOptionAttribute.cs index 362ec54..8339bf5 100644 --- a/Core/NetArgumentParser/Attributes/EnumValueOptionAttribute.cs +++ b/Core/NetArgumentParser/Attributes/EnumValueOptionAttribute.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Reflection; using NetArgumentParser.Generators; using NetArgumentParser.Options; @@ -27,7 +28,9 @@ public EnumValueOptionAttribute( bool useDefaultChoices = true, string[]? aliases = null, T[]? choices = null, - string[]? beforeParseChoices = null) + string[]? beforeParseChoices = null, + bool addChoicesToDescription = false, + bool addBeforeParseChoicesToDescription = false) : base( defaultValue, longName ?? throw new ArgumentNullException(nameof(longName)), @@ -40,7 +43,9 @@ public EnumValueOptionAttribute( ignoreCaseInChoices, aliases, choices, - beforeParseChoices) + beforeParseChoices, + addChoicesToDescription, + addBeforeParseChoicesToDescription) { UseDefaultChoices = useDefaultChoices; } @@ -58,7 +63,9 @@ public EnumValueOptionAttribute( bool useDefaultChoices = true, string[]? aliases = null, T[]? choices = null, - string[]? beforeParseChoices = null) + string[]? beforeParseChoices = null, + bool addChoicesToDescription = false, + bool addBeforeParseChoicesToDescription = false) : base( choices, longName ?? throw new ArgumentNullException(nameof(longName)), @@ -70,7 +77,9 @@ public EnumValueOptionAttribute( isFinal, ignoreCaseInChoices, aliases, - beforeParseChoices) + beforeParseChoices, + addChoicesToDescription, + addBeforeParseChoicesToDescription) { UseDefaultChoices = useDefaultChoices; } @@ -85,7 +94,7 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI if (!CanCreateOption(source, propertyInfo)) throw new CannotCreateOptionException(null, propertyInfo); - return new EnumValueOption( + var enumValueOption = new EnumValueOption( LongName, ShortName, Description, @@ -101,5 +110,12 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI DefaultValue, valueRestriction: null, t => propertyInfo.SetValue(source, t)); + + if (AddBeforeParseChoicesToDescription && BeforeParseChoices is not null && BeforeParseChoices.Any()) + enumValueOption.AddBeforeParseChoicesToDescription(); + else if (AddChoicesToDescription && Choices is not null && Choices.Any()) + enumValueOption.AddChoicesToDescription(); + + return enumValueOption; } } diff --git a/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs b/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs index 1f26707..348a6af 100644 --- a/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs +++ b/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using NetArgumentParser.Generators; using NetArgumentParser.Options; @@ -27,7 +28,9 @@ public ValueOptionAttribute( bool ignoreCaseInChoices = false, string[]? aliases = null, T[]? choices = null, - string[]? beforeParseChoices = null) + string[]? beforeParseChoices = null, + bool addChoicesToDescription = false, + bool addBeforeParseChoicesToDescription = false) : this( choices, longName ?? throw new ArgumentNullException(nameof(longName)), @@ -39,7 +42,9 @@ public ValueOptionAttribute( isFinal, ignoreCaseInChoices, aliases, - beforeParseChoices) + beforeParseChoices, + addChoicesToDescription, + addBeforeParseChoicesToDescription) { DefaultValue = new DefaultOptionValue(defaultValue); } @@ -55,7 +60,9 @@ public ValueOptionAttribute( bool isFinal = false, bool ignoreCaseInChoices = false, string[]? aliases = null, - string[]? beforeParseChoices = null) + string[]? beforeParseChoices = null, + bool addChoicesToDescription = false, + bool addBeforeParseChoicesToDescription = false) : this( choices: null, longName ?? throw new ArgumentNullException(nameof(longName)), @@ -67,7 +74,9 @@ public ValueOptionAttribute( isFinal, ignoreCaseInChoices, aliases, - beforeParseChoices) + beforeParseChoices, + addChoicesToDescription, + addBeforeParseChoicesToDescription) { } @@ -82,7 +91,9 @@ public ValueOptionAttribute( bool isFinal = false, bool ignoreCaseInChoices = false, string[]? aliases = null, - string[]? beforeParseChoices = null) + string[]? beforeParseChoices = null, + bool addChoicesToDescription = false, + bool addBeforeParseChoicesToDescription = false) : base( longName ?? throw new ArgumentNullException(nameof(longName)), shortName ?? throw new ArgumentNullException(nameof(shortName)), @@ -96,12 +107,17 @@ public ValueOptionAttribute( MetaVariable = metaVariable; IgnoreCaseInChoices = ignoreCaseInChoices; + AddChoicesToDescription = addChoicesToDescription; + AddBeforeParseChoicesToDescription = addBeforeParseChoicesToDescription; + Choices = choices; BeforeParseChoices = beforeParseChoices; } public string MetaVariable { get; } public bool IgnoreCaseInChoices { get; } + public bool AddChoicesToDescription { get; } + public bool AddBeforeParseChoicesToDescription { get; } public IEnumerable? Choices { get; } public IEnumerable? BeforeParseChoices { get; } @@ -117,7 +133,7 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI if (!CanCreateOption(source, propertyInfo)) throw new CannotCreateOptionException(null, propertyInfo); - return new ValueOption( + var valueOption = new ValueOption( LongName, ShortName, Description, @@ -132,5 +148,12 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI DefaultValue, null, t => propertyInfo.SetValue(source, t)); + + if (AddBeforeParseChoicesToDescription && BeforeParseChoices is not null && BeforeParseChoices.Any()) + valueOption.AddBeforeParseChoicesToDescription(); + else if (AddChoicesToDescription && Choices is not null && Choices.Any()) + valueOption.AddChoicesToDescription(); + + return valueOption; } } From ac3e421e8182894861aa1d5beb7f5a8da24a8d83 Mon Sep 17 00:00:00 2001 From: yakovypg Date: Sun, 15 Feb 2026 18:54:10 +0300 Subject: [PATCH 3/5] Update examples --- .../Program.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Examples/NetArgumentParser.Examples.ParserGenerationUsingAttributes/Program.cs b/Examples/NetArgumentParser.Examples.ParserGenerationUsingAttributes/Program.cs index 264427a..2e91850 100644 --- a/Examples/NetArgumentParser.Examples.ParserGenerationUsingAttributes/Program.cs +++ b/Examples/NetArgumentParser.Examples.ParserGenerationUsingAttributes/Program.cs @@ -103,7 +103,9 @@ public CustomParserConfig() useDefaultChoices: false, aliases: ["file-mode"], choices: [FileMode.Create, FileMode.Open], - beforeParseChoices: ["Create", "Open"]) + beforeParseChoices: ["Create", "Open"], + addChoicesToDescription: false, + addBeforeParseChoicesToDescription: true) ] [OptionGroup("complex-values", "", "")] public FileMode Mode { get; set; } @@ -174,7 +176,9 @@ public CustomParserConfig() isFinal: false, aliases: [], choices: [0, 45, 90], - beforeParseChoices: ["0", "45", "90"]) + beforeParseChoices: ["0", "45", "90"], + addChoicesToDescription: true, + addBeforeParseChoicesToDescription: false) ] [OptionGroup("values", "", "")] public double? Angle { get; set; } From 9e52b5ff256ba40ee5b84d68e337adbf3fe8557f Mon Sep 17 00:00:00 2001 From: yakovypg Date: Sun, 15 Feb 2026 19:03:30 +0300 Subject: [PATCH 4/5] Update documentation --- Documentation/ParserGenerationUsingAttributes.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Documentation/ParserGenerationUsingAttributes.md b/Documentation/ParserGenerationUsingAttributes.md index 1df4a46..bf80a89 100644 --- a/Documentation/ParserGenerationUsingAttributes.md +++ b/Documentation/ParserGenerationUsingAttributes.md @@ -72,7 +72,9 @@ internal class CustomParserConfig useDefaultChoices: false, aliases: ["file-mode"], choices: [FileMode.Create, FileMode.Open], - beforeParseChoices: ["Create", "Open"]) + beforeParseChoices: ["Create", "Open"], + addChoicesToDescription: false, + addBeforeParseChoicesToDescription: true) ] public FileMode Mode { get; set; } @@ -139,7 +141,9 @@ internal class CustomParserConfig isFinal: false, aliases: [], choices: [0, 45, 90], - beforeParseChoices: ["0", "45", "90"]) + beforeParseChoices: ["0", "45", "90"], + addChoicesToDescription: true, + addBeforeParseChoicesToDescription: false) ] public double? Angle { get; set; } } @@ -147,6 +151,8 @@ internal class CustomParserConfig internal record Point(double X, double Y, double Z); ``` +Note that attributes allow you to add choices and before parse choices to the option description (if the corresponding option supports it) using `addChoicesToDescription` and `addBeforeParseChoicesToDescription` parameters, so you don't need to find the generated options to do this. + ### Group Attributes Goups can be configured using `OptionGroupAttribute` attribute. In addition to specifying the group header and description, you should specify the group ID. It is necessary for the correct placement of options, since groups can have the same header. Options that you want to put in the same group must be marked with an attribute with the same ID. You should't specify header and description for all group attributes with same id. It is enough to do this for only one attribute. From 9146713a01068cced286646ea5c588fec01c1762 Mon Sep 17 00:00:00 2001 From: yakovypg Date: Sun, 15 Feb 2026 19:05:55 +0300 Subject: [PATCH 5/5] Update version --- Core/NetArgumentParser/NetArgumentParser.csproj | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/NetArgumentParser/NetArgumentParser.csproj b/Core/NetArgumentParser/NetArgumentParser.csproj index e79d3cd..90a5f9f 100644 --- a/Core/NetArgumentParser/NetArgumentParser.csproj +++ b/Core/NetArgumentParser/NetArgumentParser.csproj @@ -2,7 +2,7 @@ NetArgumentParser - 1.0.4 + 1.0.5 NetArgumentParser NetArgumentParser yakovypg diff --git a/README.md b/README.md index c931574..184079b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ license - version + version csharp