Skip to content

Commit 0c9fe6b

Browse files
authored
fix: show allowed values in help text for Nullable/ValueTuple<bool,T> option/argument (#390)
1 parent 569b913 commit 0c9fe6b

File tree

3 files changed

+57
-8
lines changed

3 files changed

+57
-8
lines changed

src/CommandLineUtils/HelpText/DefaultHelpTextGenerator.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,12 +402,27 @@ protected virtual string Format(CommandOption option)
402402

403403
private string[] ExtractNamesFromEnum(Type type)
404404
{
405-
if (type == null || !type.IsEnum)
405+
if (type == null)
406406
{
407407
return Util.EmptyArray<string>();
408408
}
409409

410-
return Enum.GetNames(type);
410+
if (ReflectionHelper.IsNullableType(type, out Type wrappedType))
411+
{
412+
return ExtractNamesFromEnum(wrappedType);
413+
}
414+
415+
if (ReflectionHelper.IsSpecialValueTupleType(type, out var fieldInfo))
416+
{
417+
return ExtractNamesFromEnum(fieldInfo.FieldType);
418+
}
419+
420+
if (type.IsEnum)
421+
{
422+
return Enum.GetNames(type);
423+
}
424+
425+
return Util.EmptyArray<string>();
411426
}
412427
}
413428
}

src/CommandLineUtils/Internal/ReflectionHelper.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,16 @@ public static bool IsNullableType(Type type, out Type? wrappedType)
105105
return result;
106106
}
107107

108+
public static bool IsSpecialValueTupleType(Type type, out FieldInfo fieldInfo)
109+
{
110+
var result = type.IsGenericType &&
111+
type.GetGenericTypeDefinition() == typeof(ValueTuple<,>) &&
112+
type.GenericTypeArguments[0] == typeof(bool);
113+
fieldInfo = result ? type.GetFields()[1] : null;
114+
115+
return result;
116+
}
117+
108118
private static IEnumerable<MemberInfo> GetAllMembers(Type type)
109119
{
110120
while (type != null)

test/CommandLineUtils.Tests/DefaultHelpTextGeneratorTests.cs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,17 @@ public void ShowHelp()
112112
app.Option("--rStrOpt <E>", "restricted str option desc.", CommandOptionType.SingleValue, o => o.IsRequired().Accepts().Values("Foo", "Bar"));
113113
app.Option<int>("--intOpt <E>", "int option desc.", CommandOptionType.SingleValue);
114114
app.Option<SomeEnum>("--enumOpt <E>", "enum option desc.", CommandOptionType.SingleValue);
115-
app.Option<SomeEnum>("--rEnumOpt <E>", "restricted enum option desc.", CommandOptionType.SingleValue, o => o.Accepts().Values("None", "Normal"));
115+
app.Option<SomeEnum>("--enumOpt2 <E>", "restricted enum option desc.", CommandOptionType.SingleValue, o => o.Accepts().Values("None", "Normal"));
116+
app.Option<(bool, SomeEnum)>("--enumOpt3 <E>", "nullable enum option desc.", CommandOptionType.SingleOrNoValue);
117+
app.Option<SomeEnum?>("--enumOpt4 <E>", "nullable enum option desc.", CommandOptionType.SingleOrNoValue);
116118
app.Argument("SomeStringArgument", "string arg desc.");
117119
app.Argument("RestrictedStringArgument", "restricted string arg desc.", a => a.IsRequired().Accepts().Values("Foo", "Bar"));
118120
app.Argument<SomeEnum>("SomeEnumArgument", "enum arg desc.");
119121
app.Argument<SomeEnum>("RestrictedEnumArgument", "restricted enum arg desc.", a => a.Accepts().Values("None", "Normal"));
122+
app.Argument<(bool, SomeEnum)>("SomeNullableEnumArgument", "nullable enum arg desc.");
120123
var helpText = GetHelpText(app);
121124

122-
Assert.Equal(@"Usage: [options] <SomeStringArgument> <RestrictedStringArgument> <SomeEnumArgument> <RestrictedEnumArgument>
125+
Assert.Equal(@"Usage: [options] <SomeStringArgument> <RestrictedStringArgument> <SomeEnumArgument> <RestrictedEnumArgument> <SomeNullableEnumArgument>
123126
124127
Arguments:
125128
SomeStringArgument string arg desc.
@@ -129,6 +132,8 @@ SomeEnumArgument enum arg desc.
129132
Allowed values are: None, Normal, Extreme.
130133
RestrictedEnumArgument restricted enum arg desc.
131134
Allowed values are: None, Normal.
135+
SomeNullableEnumArgument nullable enum arg desc.
136+
Allowed values are: None, Normal, Extreme.
132137
133138
Options:
134139
-?|-h|--help Show help information.
@@ -138,8 +143,12 @@ RestrictedEnumArgument restricted enum arg desc.
138143
--intOpt <E> int option desc.
139144
--enumOpt <E> enum option desc.
140145
Allowed values are: None, Normal, Extreme.
141-
--rEnumOpt <E> restricted enum option desc.
146+
--enumOpt2 <E> restricted enum option desc.
142147
Allowed values are: None, Normal.
148+
--enumOpt3[:<E>] nullable enum option desc.
149+
Allowed values are: None, Normal, Extreme.
150+
--enumOpt4[:<E>] nullable enum option desc.
151+
Allowed values are: None, Normal, Extreme.
143152
144153
",
145154
helpText,
@@ -154,7 +163,7 @@ public void ShowHelpFromAttributes()
154163
app.Conventions.UseDefaultConventions();
155164
var helpText = GetHelpText(app);
156165

157-
Assert.Equal(@"Usage: test [options] <SomeStringArgument> <RestrictedStringArgument> <SomeEnumArgument> <RestrictedEnumArgument>
166+
Assert.Equal(@"Usage: test [options] <SomeStringArgument> <RestrictedStringArgument> <SomeEnumArgument> <RestrictedEnumArgument> <SomeNullableEnumArgument>
158167
159168
Arguments:
160169
SomeStringArgument string arg desc.
@@ -164,6 +173,8 @@ SomeEnumArgument enum arg desc.
164173
Allowed values are: None, Normal, Extreme.
165174
RestrictedEnumArgument restricted enum arg desc.
166175
Allowed values are: None, Normal.
176+
SomeNullableEnumArgument nullable enum arg desc.
177+
Allowed values are: None, Normal, Extreme.
167178
168179
Options:
169180
-strOpt|--str-opt <STR_OPT> str option desc.
@@ -172,8 +183,12 @@ RestrictedEnumArgument restricted enum arg desc.
172183
-intOpt|--int-opt <INT_OPT> int option desc.
173184
-enumOpt|--verbosity <VERBOSITY> enum option desc.
174185
Allowed values are: None, Normal, Extreme.
175-
-rEnumOpt|--verb2 <VERB2> restricted enum option desc.
186+
-enumOpt2|--verb2 <VERB2> restricted enum option desc.
176187
Allowed values are: None, Normal.
188+
-enumOpt3|--verb3[:<VERB3>] nullable enum option desc.
189+
Allowed values are: None, Normal, Extreme.
190+
-enumOpt4|--verb4[:<VERB4>] nullable enum option desc.
191+
Allowed values are: None, Normal, Extreme.
177192
-?|-h|--help Show help information.
178193
179194
",
@@ -197,10 +212,16 @@ public class MyApp
197212
[Option(ShortName = "enumOpt", Description = "enum option desc.")]
198213
public SomeEnum Verbosity { get; set; }
199214

200-
[Option(ShortName = "rEnumOpt", Description = "restricted enum option desc.")]
215+
[Option(ShortName = "enumOpt2", Description = "restricted enum option desc.")]
201216
[AllowedValues("None", "Normal")]
202217
public SomeEnum Verb2 { get; set; }
203218

219+
[Option(ShortName = "enumOpt3", Description = "nullable enum option desc.")]
220+
public (bool HasValue, SomeEnum Value) Verb3 { get; set; }
221+
222+
[Option(CommandOptionType.SingleOrNoValue, ShortName = "enumOpt4", Description = "nullable enum option desc.")]
223+
public SomeEnum? Verb4 { get; set; }
224+
204225
[Argument(0, Description = "string arg desc.")]
205226
public string SomeStringArgument { get; set; }
206227

@@ -215,6 +236,9 @@ public class MyApp
215236
[Argument(3, Description = "restricted enum arg desc.")]
216237
[AllowedValues("None", "Normal")]
217238
public SomeEnum RestrictedEnumArgument { get; set; }
239+
240+
[Argument(4, Description = "nullable enum arg desc.")]
241+
public (bool HasValue, SomeEnum Value) SomeNullableEnumArgument { get; set; }
218242
}
219243

220244
[Theory]

0 commit comments

Comments
 (0)