Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

default value for option/argument (and show it in the help text) #389

13 changes: 4 additions & 9 deletions src/CommandLineUtils/Abstractions/ValueParserProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public IValueParser GetParser(Type type)
return parser;
}

if (ReflectionHelper.IsNullableType(type, out var wrappedType) && wrappedType != null)
if (ReflectionHelper.IsNullableType(type, out var wrappedType))
{
if (wrappedType.IsEnum)
{
Expand All @@ -114,21 +114,16 @@ public IValueParser GetParser(Type type)
}
}

if (!type.IsGenericType)
if (ReflectionHelper.IsSpecialValueTupleType(type, out var wrappedType2))
{
return null;
}

var typeDef = type.GetGenericTypeDefinition();
if (typeDef == typeof(ValueTuple<,>) && type.GenericTypeArguments[0] == typeof(bool))
{
var innerParser = GetParser(type.GenericTypeArguments[1]);
var innerParser = GetParser(wrappedType2);
if (innerParser == null)
{
return null;
}
var method = typeof(ValueTupleValueParser).GetMethod(nameof(ValueTupleValueParser.Create)).MakeGenericMethod(type.GenericTypeArguments[1]);
return (IValueParser)method.Invoke(null, new object[] { innerParser });

}

return null;
Expand Down
18 changes: 18 additions & 0 deletions src/CommandLineUtils/CommandArgument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace McMaster.Extensions.CommandLineUtils
/// <seealso cref="CommandOption"/>
public class CommandArgument
{
private string? _defaultValue;

/// <summary>
/// Initializes a new instance of <see cref="CommandArgument"/>.
/// </summary>
Expand Down Expand Up @@ -54,6 +56,22 @@ public CommandArgument()
/// </summary>
public string? Value => Values.FirstOrDefault();

/// <summary>
/// The default value of the argument.
/// </summary>
public string? DefaultValue
{
get => _defaultValue;
set
{
_defaultValue = value;
if (value != null)
{
Values.Add(value);
}
scott-xu marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// <summary>
/// A collection of validators that execute before invoking <see cref="CommandLineApplication.OnExecute(Func{int})"/>.
/// When validation fails, <see cref="CommandLineApplication.ValidationErrorHandler"/> is invoked.
Expand Down
23 changes: 23 additions & 0 deletions src/CommandLineUtils/CommandArgument{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class CommandArgument<T> : CommandArgument, IInternalCommandParamOfT
{
private readonly List<T> _parsedValues = new List<T>();
private readonly IValueParser<T> _valueParser;
private T _defaultValue;

/// <summary>
/// Initializes a new instance of <see cref="CommandArgument{T}" />
Expand All @@ -29,6 +30,7 @@ public CommandArgument(IValueParser<T> valueParser)
{
_valueParser = valueParser ?? throw new ArgumentNullException(nameof(valueParser));
UnderlyingType = typeof(T);
SetBaseDefaultValue(default);
}

/// <summary>
Expand All @@ -41,6 +43,19 @@ public CommandArgument(IValueParser<T> valueParser)
/// </summary>
public IReadOnlyList<T> ParsedValues => _parsedValues;

/// <summary>
/// The default value of the argument.
/// </summary>
public new T DefaultValue
{
get => _defaultValue;
set
{
_defaultValue = value;
SetBaseDefaultValue(value);
}
}

void IInternalCommandParamOfT.Parse(CultureInfo culture)
{
_parsedValues.Clear();
Expand All @@ -49,5 +64,13 @@ void IInternalCommandParamOfT.Parse(CultureInfo culture)
_parsedValues.Add(_valueParser.Parse(Name, t, culture));
}
}

void SetBaseDefaultValue(T value)
{
if (!ReflectionHelper.IsSpecialValueTupleType(typeof(T), out _))
{
base.DefaultValue = value?.ToString();
}
}
}
}
5 changes: 5 additions & 0 deletions src/CommandLineUtils/CommandLineApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ internal CommandLineApplication(CommandLineApplication parent, string name)

_conventionContext = CreateConventionContext();

this.Initialize();
}

internal virtual void Initialize()
{
if (Parent != null)
{
foreach (var convention in Parent._conventions)
Expand Down
8 changes: 2 additions & 6 deletions src/CommandLineUtils/CommandLineApplication{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ public class CommandLineApplication<TModel> : CommandLineApplication, IModelAcce
public CommandLineApplication()
: base()
{
Initialize();
}

/// <summary>
Expand All @@ -35,7 +34,6 @@ public CommandLineApplication()
public CommandLineApplication(IConsole console)
: base(console)
{
Initialize();
}

/// <summary>
Expand All @@ -46,7 +44,6 @@ public CommandLineApplication(IConsole console)
public CommandLineApplication(IConsole console, string workingDirectory)
: base(console, workingDirectory)
{
Initialize();
}

/// <summary>
Expand All @@ -64,18 +61,17 @@ public CommandLineApplication(IConsole console, string workingDirectory)
public CommandLineApplication(IHelpTextGenerator helpTextGenerator, IConsole console, string workingDirectory)
: base(helpTextGenerator, console, workingDirectory)
{
Initialize();
}

internal CommandLineApplication(CommandLineApplication parent, string name)
: base(parent, name)
{
Initialize();
}

private void Initialize()
internal override void Initialize()
{
_lazy = new Lazy<TModel>(CreateModel);
base.Initialize();
}

private static TModel DefaultModelFactory()
Expand Down
18 changes: 18 additions & 0 deletions src/CommandLineUtils/CommandOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ namespace McMaster.Extensions.CommandLineUtils
/// </summary>
public class CommandOption
{
private string? _defaultValue;

/// <summary>
/// Initializes a new <see cref="CommandOption"/>.
/// </summary>
Expand Down Expand Up @@ -101,6 +103,22 @@ internal CommandOption(CommandOptionType type)
/// </summary>
public List<string?> Values { get; } = new List<string?>();

/// <summary>
/// The default value of the option.
/// </summary>
public string? DefaultValue
{
get => _defaultValue;
set
{
_defaultValue = value;
if (value != null)
{
Values.Add(value);
}
scott-xu marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// <summary>
/// Defines the type of the option.
/// </summary>
Expand Down
24 changes: 24 additions & 0 deletions src/CommandLineUtils/CommandOption{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using McMaster.Extensions.CommandLineUtils.Abstractions;

namespace McMaster.Extensions.CommandLineUtils
Expand All @@ -19,6 +20,7 @@ public class CommandOption<T> : CommandOption, IInternalCommandParamOfT
{
private readonly List<T> _parsedValues = new List<T>();
private readonly IValueParser<T> _valueParser;
private T _defaultValue;

/// <summary>
/// Initializes a new instance of <see cref="CommandOption{T}" />
Expand All @@ -31,6 +33,7 @@ public CommandOption(IValueParser<T> valueParser, string template, CommandOption
{
_valueParser = valueParser ?? throw new ArgumentNullException(nameof(valueParser));
UnderlyingType = typeof(T);
SetBaseDefaultValue(default);
}

/// <summary>
Expand All @@ -43,6 +46,19 @@ public CommandOption(IValueParser<T> valueParser, string template, CommandOption
/// </summary>
public IReadOnlyList<T> ParsedValues => _parsedValues;

/// <summary>
/// The default value of the option.
/// </summary>
public new T DefaultValue
{
get => _defaultValue;
set
{
_defaultValue = value;
SetBaseDefaultValue(value);
}
}

void IInternalCommandParamOfT.Parse(CultureInfo culture)
{
_parsedValues.Clear();
Expand All @@ -51,5 +67,13 @@ void IInternalCommandParamOfT.Parse(CultureInfo culture)
_parsedValues.Add(_valueParser.Parse(LongName ?? ShortName ?? SymbolName, t, culture));
}
}

void SetBaseDefaultValue(T value)
{
if (!ReflectionHelper.IsSpecialValueTupleType(typeof(T), out _))
{
base.DefaultValue = value?.ToString();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ public virtual void Apply(ConventionContext context)
argPropOrder.Add(argumentAttr.Order, prop);
argOrder.Add(argumentAttr.Order, argument);

var getter = ReflectionHelper.GetPropertyGetter(prop);

if (!ReflectionHelper.IsSpecialValueTupleType(prop.PropertyType, out _))
{
argument.DefaultValue = getter.Invoke(convention.ModelAccessor.GetModel())?.ToString();
}

var setter = ReflectionHelper.GetPropertySetter(prop);

if (argument.MultipleValues)
Expand Down
12 changes: 12 additions & 0 deletions src/CommandLineUtils/Conventions/OptionAttributeConventionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ private protected void AddOption(ConventionContext context, CommandOption option
context.Application._longOptions.Add(option.LongName, prop);
}

if (option.OptionType != CommandOptionType.NoValue)
{
var getter = ReflectionHelper.GetPropertyGetter(prop);

var value = getter.Invoke(modelAccessor.GetModel());
scott-xu marked this conversation as resolved.
Show resolved Hide resolved

if (!ReflectionHelper.IsSpecialValueTupleType(prop.PropertyType, out _))
{
option.DefaultValue = value?.ToString();
}
}

var setter = ReflectionHelper.GetPropertySetter(prop);

switch (option.OptionType)
Expand Down
14 changes: 12 additions & 2 deletions src/CommandLineUtils/HelpText/DefaultHelpTextGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ public virtual void Generate(CommandLineApplication application, TextWriter outp
}
}

if (!string.IsNullOrEmpty(arg.DefaultValue))
{
description += $"\nDefault value is: {arg.DefaultValue}.";
}

var wrappedDescription = IndentWriter?.Write(description);
var message = string.Format(outputFormat, arg.Name, wrappedDescription);

Expand Down Expand Up @@ -266,6 +271,11 @@ public virtual void Generate(CommandLineApplication application, TextWriter outp
}
}

if (!string.IsNullOrEmpty(opt.DefaultValue))
{
description += $"\nDefault value is: {opt.DefaultValue}.";
}

var wrappedDescription = IndentWriter?.Write(description);
var message = string.Format(outputFormat, Format(opt), wrappedDescription);

Expand Down Expand Up @@ -412,9 +422,9 @@ private string[] ExtractNamesFromEnum(Type type)
return ExtractNamesFromEnum(wrappedType);
}

if (ReflectionHelper.IsSpecialValueTupleType(type, out var fieldInfo))
if (ReflectionHelper.IsSpecialValueTupleType(type, out var wrappedType2))
{
return ExtractNamesFromEnum(fieldInfo.FieldType);
return ExtractNamesFromEnum(wrappedType2);
}

if (type.IsEnum)
Expand Down
28 changes: 12 additions & 16 deletions src/CommandLineUtils/Internal/CommandOptionTypeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,28 +55,24 @@ public CommandOptionType GetOptionType(Type clrType, ValueParserProvider? valueP
return CommandOptionType.SingleValue;
}

if (clrType.IsGenericType)
if (ReflectionHelper.IsNullableType(clrType, out var wrappedType))
{
var typeDef = clrType.GetGenericTypeDefinition();
if (typeDef == typeof(Nullable<>))
{
return GetOptionType(clrType.GetGenericArguments().First(), valueParsers);
}
return GetOptionType(wrappedType, valueParsers);
}

if (typeDef == typeof(Tuple<,>) && clrType.GenericTypeArguments[0] == typeof(bool))
if (ReflectionHelper.IsSpecialValueTupleType(clrType, out var wrappedType2))
{
if (GetOptionType(wrappedType2, valueParsers) == CommandOptionType.SingleValue)
{
if (GetOptionType(clrType.GenericTypeArguments[1], valueParsers) == CommandOptionType.SingleValue)
{
return CommandOptionType.SingleOrNoValue;
}
return CommandOptionType.SingleOrNoValue;
}
}

if (typeDef == typeof(ValueTuple<,>) && clrType.GenericTypeArguments[0] == typeof(bool))
if (ReflectionHelper.IsSpecialTupleType(clrType, out var wrappedType3))
{
if (GetOptionType(wrappedType3, valueParsers) == CommandOptionType.SingleValue)
{
if (GetOptionType(clrType.GenericTypeArguments[1], valueParsers) == CommandOptionType.SingleValue)
{
return CommandOptionType.SingleOrNoValue;
}
return CommandOptionType.SingleOrNoValue;
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/CommandLineUtils/Internal/Delegates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
namespace McMaster.Extensions.CommandLineUtils
{
internal delegate void SetPropertyDelegate(object obj, object? value);

internal delegate object GetPropertyDelegate(object obj);
}