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

Feature request: provide default value for option (and show it in the help) #82

Closed
ghost opened this issue Apr 7, 2018 · 19 comments · Fixed by #389 or #421
Closed

Feature request: provide default value for option (and show it in the help) #82

ghost opened this issue Apr 7, 2018 · 19 comments · Fixed by #389 or #421
Assignees
Labels
closed-resolved This issue is closed because the work done to resolve it is complete. enhancement help wanted We would be willing to take a well-written PR to help fix this.
Milestone

Comments

@ghost
Copy link

ghost commented Apr 7, 2018

Apologies if I've missed this one. I see in the examples you're using conditionals to set default values, but what about providing something like

[Option(Description = "Verbosity",DefaultValue = 5)] so when an option is not required a default value is supplied - this could also be reflected in the help text so we're following the Principle of Least Astonishment.

@ghost
Copy link
Author

ghost commented Apr 7, 2018

I found https://github.com/natemcmaster/CommandLineUtils/blob/master/samples/Attributes/Program.cs#L19 which is pretty much what I want. I guess the help text can't be easily generated from there.

@ghost ghost closed this as completed Apr 7, 2018
@liamdawson
Copy link
Contributor

liamdawson commented May 23, 2018

That being said, I would like it if it could infer the default value from the target property, as sometimes I want default values that can be statically set, but can't be set as a constant (e.g. a path that is in the user's home directory).

For example:

[Option("--data-directory")]
public string DataDirectory { get; set; } = Path.Combine(Environment.GetSpecialDirectoryPath(Environment.SpecialDirectories.UserProfile), ".cache/app");

(typed from memory, may not compile)

My hope is that I could add a ShowDefault = true to the OptionAttribute and have it append [default: /home/liamdawson/.cache/app] to the help text.

@dbogatov
Copy link

Could we reopen this?
I'm glad the original author has found his workaround, but I would indeed ask that the default value is printed.

@natemcmaster natemcmaster reopened this Nov 19, 2019
@natemcmaster natemcmaster added enhancement help wanted We would be willing to take a well-written PR to help fix this. labels Nov 19, 2019
@natemcmaster
Copy link
Owner

natemcmaster commented Nov 19, 2019

Marking this as up-for grabs. I would be okay adding logic to print the default CLR value in the help. I'm not sure yet about adding new properties to OptionAttribute -- I think default property values are a more natural way to express this in C# -- but I would be okay if we added string DefaultValue { get; set; } to CommandOption for the builder API users.

@natemcmaster natemcmaster added this to the 3.0 milestone Jan 5, 2020
@SeriousM
Copy link

My current workaround is to set the default value on the property, don't mark it with Required (as this would stop the execution because the option was not set in the command line) and call Validator.Validate(this, new ValidationContext(this)) at the beginning of OnExecuteAsync to ensure that all the other attrubutes are checked, like FileExists or Url.

@natemcmaster natemcmaster removed this from the 3.0 milestone Mar 21, 2020
@EajksEajks
Copy link

And how would you set the default value using the builder API?

@scott-xu
Copy link
Contributor

I made a PR #369 about showing allowed values in help text which is somewhat similar with this issue.

@scott-xu
Copy link
Contributor

scott-xu commented Jun 14, 2020

Here's my proposal
API:

public class CommandOption
{
   // other members

   public Func<object> DefaultValue { get; set; }
}

Usage
builder:

var app = new CommandLineApplication();
app.Option("-o|--opt", "Some description.", CommandOptionType.SingleValue, o => o.DefaultValue = () => "foo");

attribute:

class Program
{
    [Option("-o|--opt", "Some description.", CommandOptionType.SingleValue)]
    public string? Option { get; } = "foo";
}

help text

Options:
  -o|--opt        Some description.
                  Default value is: foo.

@scott-xu
Copy link
Contributor

We may need re-consider about the below implementation, or leave it as is:

public class CommandOption
{
   // other members

   public bool HasValue()
   {
       return Values.Any();
   }

   public string? Value()
   {
       return HasValue() ? Values[0] : null;
   }
}

@scott-xu
Copy link
Contributor

Do you have any comments? @natemcmaster

@natemcmaster
Copy link
Owner

I prefer to avoid using object in API. I think its too ambiguous. What about this instead?

public class CommandOption
{
   // other members

       public string? DefaultValue { get; set; }
}

public class CommandOption<T>
{
        private T _defaultValue;

        public new T DefaultValue
        {
            get => _defaultValue;
            set
            {
                _defaultValue = value;
                base.DefaultValue = value?.ToString();
            }
        }
}

@scott-xu
Copy link
Contributor

scott-xu commented Jun 28, 2020

I proposed object instead of string because I wanted to avoid converting, especially for attribute API. The most institute way to populate a default value is

    [Option("-o|--opt", "Some description.", CommandOptionType.SingleValue)]
    public SomeType? Option { get; } = SomeExpressionToBuildAValueOfSomeType;

I’ll rethink about it.

@scott-xu
Copy link
Contributor

The problem here is the logic about Value() and HasValue()

/// <summary>
/// True when <see cref="Values"/> is not empty.
/// </summary>
/// <returns></returns>
public bool HasValue()
{
    return Values.Any(); // Should we continue checking if the default value is set?
}

/// <summary>
/// Returns the first element of <see cref="Values"/>, if any.
/// </summary>
/// <returns></returns>
public string? Value()
{
    return HasValue() ? Values[0] : this.Default; // Sure we can return the first element of values, if any, and fall back to default value, if it has.
}

public string? DefaultValue { get; set; }

@scott-xu
Copy link
Contributor

  • I didn't change any logic about Value() and HasValue()
  • Should we do the same for CommandArgument?

@scott-xu
Copy link
Contributor

Default value for CommandArgument is added.

@natemcmaster
Copy link
Owner

The problem here is the logic about Value() and HasValue()

I think the least-surprising behavior here would be to update Value and HasValue.

For CommandOption:

  • If DefaultValue is provided, HasValue is always true.
  • Value returns what the user specified, or DefaultValue if nothing was given.

For CommandArgument:

  • If DefaultValue is provided, arg.Values returns what the user specified, or new List<string> { DefaultValue }.
  • arg.Value code remains the same as it's just sugar for getting the first item from arg.Values

@scott-xu
Copy link
Contributor

I checked the code and I think we can share the same logic:
If DefaultValue is provided, add the default value to the Values list.

@natemcmaster
Copy link
Owner

This issue is resolved and available now in this beta: https://www.nuget.org/packages/McMaster.Extensions.CommandLineUtils/4.0.0-beta.56. Please take a look and let me know if you have feedback on its behavior.

@natemcmaster natemcmaster added the closed-resolved This issue is closed because the work done to resolve it is complete. label Jan 18, 2021
@meywd
Copy link

meywd commented Sep 12, 2021

@natemcmaster First thank you for this library, its really helpful.

Regarding Default values, since this is in beta, I think the sample code at the front page shouldn't include DefaultValue or at least add a comment saying that its in beta, to not confuse users.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-resolved This issue is closed because the work done to resolve it is complete. enhancement help wanted We would be willing to take a well-written PR to help fix this.
Projects
None yet
7 participants