From 3fd7d2fbe2b73224d7e35cb2be85757a5ac91799 Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sun, 31 May 2020 18:31:28 +0800 Subject: [PATCH] Show allowed values in help text Similar with enum type, if a string argument/option accepts limited value(s), let's show the allowed values in help text. --- .../Attributes/AllowedValuesAttribute.cs | 3 ++ .../HelpText/DefaultHelpTextGenerator.cs | 15 +++++++ .../Validation/AttributeValidator.cs | 2 + .../DefaultHelpTextGeneratorTests.cs | 40 ++++++++++++++----- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/CommandLineUtils/Attributes/AllowedValuesAttribute.cs b/src/CommandLineUtils/Attributes/AllowedValuesAttribute.cs index 81af784e..c67c25ec 100644 --- a/src/CommandLineUtils/Attributes/AllowedValuesAttribute.cs +++ b/src/CommandLineUtils/Attributes/AllowedValuesAttribute.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.ObjectModel; using System.ComponentModel.DataAnnotations; namespace McMaster.Extensions.CommandLineUtils @@ -28,6 +29,8 @@ public AllowedValuesAttribute(params string[] allowedValues) { } + internal ReadOnlyCollection AllowedValues => Array.AsReadOnly(this._allowedValues); + /// /// Initializes an instance of . /// diff --git a/src/CommandLineUtils/HelpText/DefaultHelpTextGenerator.cs b/src/CommandLineUtils/HelpText/DefaultHelpTextGenerator.cs index 456077e9..79913467 100644 --- a/src/CommandLineUtils/HelpText/DefaultHelpTextGenerator.cs +++ b/src/CommandLineUtils/HelpText/DefaultHelpTextGenerator.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Reflection; using System.Text; +using McMaster.Extensions.CommandLineUtils.Validation; namespace McMaster.Extensions.CommandLineUtils.HelpText { @@ -196,6 +197,13 @@ protected virtual void GenerateArguments( ? $"{arg.Description}\nAllowed values are: {string.Join(", ", enumNames)}." : arg.Description; + var attributeValidator = arg.Validators.Cast().FirstOrDefault(); + + if (attributeValidator != null && attributeValidator.ValidationAttribute is AllowedValuesAttribute allowedValuesAttribute) + { + description += $"\nAllowed values are: {string.Join(", ", allowedValuesAttribute.AllowedValues)}."; + } + var wrappedDescription = IndentWriter?.Write(description); var message = string.Format(outputFormat, arg.Name, wrappedDescription); @@ -231,6 +239,13 @@ protected virtual void GenerateOptions( ? $"{opt.Description}\nAllowed values are: {string.Join(", ", enumNames)}." : opt.Description; + var attributeValidator = opt.Validators.Cast().FirstOrDefault(); + + if (attributeValidator != null && attributeValidator.ValidationAttribute is AllowedValuesAttribute allowedValuesAttribute) + { + description+= $"\nAllowed values are: {string.Join(", ", allowedValuesAttribute.AllowedValues)}."; + } + var wrappedDescription = IndentWriter?.Write(description); var message = string.Format(outputFormat, Format(opt), wrappedDescription); diff --git a/src/CommandLineUtils/Validation/AttributeValidator.cs b/src/CommandLineUtils/Validation/AttributeValidator.cs index ba1773b4..88297977 100644 --- a/src/CommandLineUtils/Validation/AttributeValidator.cs +++ b/src/CommandLineUtils/Validation/AttributeValidator.cs @@ -24,6 +24,8 @@ public AttributeValidator(ValidationAttribute attribute) _attribute = attribute ?? throw new ArgumentNullException(nameof(attribute)); } + internal ValidationAttribute ValidationAttribute => this._attribute; + /// /// Gets the validation result for a command line option. /// diff --git a/test/CommandLineUtils.Tests/DefaultHelpTextGeneratorTests.cs b/test/CommandLineUtils.Tests/DefaultHelpTextGeneratorTests.cs index 9eee7235..486c29bb 100644 --- a/test/CommandLineUtils.Tests/DefaultHelpTextGeneratorTests.cs +++ b/test/CommandLineUtils.Tests/DefaultHelpTextGeneratorTests.cs @@ -108,25 +108,31 @@ public void ShowHelp() var app = new CommandLineApplication(); app.HelpOption(); app.Option("--strOpt ", "str option desc.", CommandOptionType.SingleValue); + app.Option("--rStrOpt ", "restricted str option desc.", CommandOptionType.SingleValue, o => o.Accepts().Values("Foo", "Bar")); app.Option("--intOpt ", "int option desc.", CommandOptionType.SingleValue); app.Option("--enumOpt ", "enum option desc.", CommandOptionType.SingleValue); app.Argument("SomeStringArgument", "string arg desc."); + app.Argument("RestrictedStringArgument", "restricted string arg desc.", a=>a.Accepts().Values("Foo", "Bar")); app.Argument("SomeEnumArgument", "enum arg desc."); var helpText = GetHelpText(app); - Assert.Equal(@"Usage: [options] + Assert.Equal(@"Usage: [options] Arguments: - SomeStringArgument string arg desc. - SomeEnumArgument enum arg desc. - Allowed values are: None, Normal, Extreme. + SomeStringArgument string arg desc. + RestrictedStringArgument restricted string arg desc. + Allowed values are: Foo, Bar. + SomeEnumArgument enum arg desc. + Allowed values are: None, Normal, Extreme. Options: - -?|-h|--help Show help information. - --strOpt str option desc. - --intOpt int option desc. - --enumOpt enum option desc. - Allowed values are: None, Normal, Extreme. + -?|-h|--help Show help information. + --strOpt str option desc. + --rStrOpt restricted str option desc. + Allowed values are: Foo, Bar. + --intOpt int option desc. + --enumOpt enum option desc. + Allowed values are: None, Normal, Extreme. ", helpText, @@ -141,15 +147,19 @@ public void ShowHelpFromAttributes() app.Conventions.UseDefaultConventions(); var helpText = GetHelpText(app); - Assert.Equal(@"Usage: test [options] + Assert.Equal(@"Usage: test [options] Arguments: SomeStringArgument string arg desc. + RestrictedStringArgument restricted string arg desc. + Allowed values are: Foo, Bar. SomeEnumArgument enum arg desc. Allowed values are: None, Normal, Extreme. Options: -strOpt|--str-opt str option desc. + -rStrOpt|--r-str-opt restricted str option desc. + Allowed values are: Foo, Bar. -intOpt|--int-opt int option desc. -enumOpt|--verbosity enum option desc. Allowed values are: None, Normal, Extreme. @@ -165,6 +175,10 @@ public class MyApp [Option(ShortName = "strOpt", Description = "str option desc.")] public string strOpt { get; set; } + [Option(ShortName = "rStrOpt", Description = "restricted str option desc.")] + [AllowedValues("Foo", "Bar")] + public string rStrOpt { get; set; } + [Option(ShortName = "intOpt", Description = "int option desc.")] public int intOpt { get; set; } @@ -174,7 +188,11 @@ public class MyApp [Argument(0, Description = "string arg desc.")] public string SomeStringArgument { get; set; } - [Argument(1, Description = "enum arg desc.")] + [Argument(1, Description = "restricted string arg desc.")] + [AllowedValues("Foo", "Bar")] + public string RestrictedStringArgument { get; set; } + + [Argument(2, Description = "enum arg desc.")] public SomeEnum SomeEnumArgument { get; set; } }