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

Introduce option groups #552

Merged
merged 14 commits into from
Dec 18, 2019

Conversation

hadzhiyski
Copy link
Contributor

@hadzhiyski hadzhiyski commented Dec 8, 2019

I found a missing use case in the commandline library.
When one or more options has group set, at least one of these properties should have set value (they behave as required)

Example:

[Option("option1", Required = true)]
public string Option1 { get; set; }

[Option("option2", Group="special")]
public string Option2 { get; set; }

[Option("option3", Group="special")]
public string Option3 { get; set; }
myapp --option1="test" --option2="asd" --option3="qwerty" // OK
myapp --option1="test" --option2="asd" // OK
myapp --option1="test" --option3="qwerty" // OK
myapp --option1="test" // Error: MissingGroupOptionError 
Error text: "At least one option in group 'special' must have value"

I hope this makes sense.

Closes #546

When one or more options has group set, at least one of these properties should have set value (they behave as required)
hadzhiyski added a commit to hadzhiyski/FileWarden that referenced this pull request Dec 8, 2019
Submodule branch feature/group-options provides support to group options. If the branch is merged to master and published at NuGet the submodule will be removed. 
More details at commandlineparser/commandline#552
@hadzhiyski hadzhiyski changed the base branch from master to develop December 10, 2019 07:10
@hadzhiyski
Copy link
Contributor Author

hadzhiyski commented Dec 10, 2019

Could you review this and approve this PR if you think it is worth introducing the feature? For me it is useful, because I need it for a project of mine - https://github.com/hadzhiyski/FileWarden/tree/develop

@moh-hassan
Copy link
Collaborator

Thanks @hadzhiyski for your work.

As I understand group (correct me):

  • group is like Mutual Exclusive Options except in group at least one option in group is required(they need not to be defined as required).

  • you can use more than one option in the same group (even all options ) at a time.

The following use case using enum may be equivalent for the group:

class Options
{
[Option("option1", Required = true)]
public string Option1 { get; set; }

//use multi enum
[Option("special", Required = true)]
public IEnumerable<Special> SpecialOption { get; set; }
}

//stndardize option values using enum
enum Special 
{
asd,
qwerty
}

Mapping you test cases ,

This is valid command:

 myapp --option1 test --special asd  //ok only one special option
 myapp --option1 test --special qwerty  //ok only another one special option
 myapp --option1 test --special asd qwerty //ok both special options

This is not valid:

 myapp --option1 test  //Error  no Special option is provided (required)

Can this approach help for your use case?

@hadzhiyski
Copy link
Contributor Author

Hello,

This is a useful solution. However it's not exactly what I am trying to achieve.
Imagine an application which appends prefix and/or suffix to a filename. Basically renames the file with prefix and/or suffix. Both options ("prefix" and "suffix") belong to the verb rename, so I am trying to achieve the following

myapp rename --source C:\test\file1.txt --prefix test --suffix _10122019 // OK. We have both options
myapp rename --source C:\test\file1.txt --prefix test // OK. We have prefix option only
myapp rename --source C:\test\file1.txt --suffix _10122019 // OK. We have suffix option only

But

myapp rename --source C:\test\file1.txt // Error: Neither prefix nor suffix is available

Mutually exclusive sets allow me use only one of the two options, but never together.
Your solution might be useful if I want to pass multiple values to either to the prefix or suffix option.

How does this sound to you? I hope it makes sense.

@moh-hassan
Copy link
Collaborator

I give it a quick run test.It seems it's useful and good.
Kindly, can you cover the following points:

  • Provide unit test that cover the different use-cases including exception and helpText.
  • What about more than one group? It seems it support more than one (good).
  • A wiki page to explain the value of the group with code samples?
  • Is there a break change? It seems that there is a break change in some tests?
  • Can this feature help the concept of SubVerbs as explained here (just for information, need not change in your PR)

@hadzhiyski
Copy link
Contributor Author

hadzhiyski commented Dec 12, 2019

@moh-hassan How I could update the wiki? I cloned it, but when I try to push the changes, I am getting 'access denied' error. Could you add me to commandlineparser organisation or I can send you the changes here and you to update these for me later?

@moh-hassan
Copy link
Collaborator

You can follow the instructions of How to contribute to Wiki Documentation and attach the git patch file in PR.

@hadzhiyski
Copy link
Contributor Author

Hello,
Here are the wiki updates. option-groups.zip

@hadzhiyski
Copy link
Contributor Author

hadzhiyski commented Dec 13, 2019

  • Provide unit test that cover the different use-cases including exception and helpText.
  • What about more than one group? It seems it support more than one (good).

We can use a single group on a single option which basically acts like required. Multiple groups can be used on multiple options and they are separated from each other.

  • A wiki page to explain the value of the group with code samples?
  • Is there a break change? It seems that there is a break change in some tests?

Group parameter is added to OptionSpecification and OptionAttribute

  • Can this feature help the concept of SubVerbs as explained here (just for information, need not change in your PR)

In my opinion there is no connection between the two functionalities. Option groups are on 'option' level and sub verbs are on 'verb' level.

@moh-hassan
Copy link
Collaborator

moh-hassan commented Dec 13, 2019

Thanks @hadzhiyski for the update.
The wiki patch is applied successfully :) and it seems good.

There are some missing unit tests:

  1. Required

Required rules are ignored if an option belongs to an option group.

Need a unit test

  1. Default Value
    a-Need a unit test
    b- It's failed and should success if no values is provided for the group

    At least one option from group 'append' (prefix, suffix) is required.

Group should take Default into account.
(if (option ==null) option = Default)

  1. What do you think for the priority of SetName /Group, which one can be ignored?
    [ ] ignore SetName in favor of Group.
    [ ] ignore Group in favor of SetName.
    [ ] Raise Error: Only SetName or Group can be used. The two are not allowed. (Preferred)

  2. What is the effect of option with group if it's hide? (ignore all hiding options)

Edit.

@hadzhiyski
Copy link
Contributor Author

Group should take Default into account.
(if (option ==null) option = Default)

@moh-hassan Could you give me more details about this? I can not understand what are you trying to say. Could you send a code sample or where this is located in source?

3. What do you think for the priority of SetName /Group, which one can be ignored?
Raise Error: Only SetName or Group can be used. The two are not allowed.

I also think the best way will be to raise error.

@moh-hassan
Copy link
Collaborator

moh-hassan commented Dec 16, 2019

@hadzhiyski

Group should take Default into account.
(if (option ==null) option = Default)

It means that when using Group for option and that Option has Default like:

[Option("option2", Group="special", Default="xyz")]
public string Option2 { get; set; }

Group should take the Default value "xyz" into account.
I suggest you a resolution for this issue using a Pseudo code rule (not included in your source and like c# code)

//Enforce this rule for the group
if (option.Value ==null) option.Value = Option.Default)

You are free to implement this rule.
I think you may be confused with Default because you are using this class name: Simple_Options_With_OptionGroup_WithDefaultValue which isn't related to Default :)

@moh-hassan
Copy link
Collaborator

@hadzhiyski
Kindly, Stop changing your PR because I start reviewing yours.
I will ,a little time, send you the comments of review with the suggested changes.

Copy link
Collaborator

@moh-hassan moh-hassan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest change the type of Group from Maybe to string, for the following reasons:

  • Group is definded in OptionAttribute Class as string and here

  • It's like string SetName which is initialized to string.Empty

  • There is no null processing /or Mapping in the Group.It's just only a name.

  • You need not to do such expressions:

        string.IsNullOrWhiteSpace(attribute.Group) ? Maybe.Nothing<string>() : Maybe.Just(attribute.Group),
    

I did these modification in my side and test pass except 3 tests in the multi group(not yet reviewed).

src/CommandLine/Core/OptionSpecification.cs Outdated Show resolved Hide resolved
src/CommandLine/Core/OptionSpecification.cs Outdated Show resolved Hide resolved
src/CommandLine/Core/OptionSpecification.cs Outdated Show resolved Hide resolved
src/CommandLine/Core/SpecificationPropertyRules.cs Outdated Show resolved Hide resolved
src/CommandLine/Core/SpecificationPropertyRules.cs Outdated Show resolved Hide resolved
src/CommandLine/Core/SpecificationPropertyRules.cs Outdated Show resolved Hide resolved
src/CommandLine/Core/OptionSpecification.cs Outdated Show resolved Hide resolved
src/CommandLine/Core/OptionSpecification.cs Outdated Show resolved Hide resolved
src/CommandLine/Text/HelpText.cs Outdated Show resolved Hide resolved
Copy link
Collaborator

@moh-hassan moh-hassan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All Test passed including the 3 multi group help test.

If you want, I can update your PR with a commit: "Modify type of Group to string instead of Mayb<string>"

src/CommandLine/Text/HelpText.cs Outdated Show resolved Hide resolved
@hadzhiyski
Copy link
Contributor Author

@moh-hassan Thanks for the review.
If you already have the commit, please do it. Otherwise I will make the changes.

@moh-hassan
Copy link
Collaborator

@hadzhiyski
Copy link
Contributor Author

Brilliant. Thank you so much!

@moh-hassan moh-hassan merged commit 7e6325d into commandlineparser:develop Dec 18, 2019
@hadzhiyski
Copy link
Contributor Author

@moh-hassan Thank you for the great work!
In which version I could expect this feature to be included?

@hadzhiyski hadzhiyski deleted the feature/group-options branch December 19, 2019 16:15
@sm-g
Copy link

sm-g commented Feb 2, 2020

@moh-hassan cli is confusing now

for such options:

internal class Options
{
	[Option('u', "url", SetName = "url", Group = "input", Default = "http://localhost:5002/swagger/current/swagger.json")]
	public string SwaggerUrl { get; set; }

	[Option('j', "json", SetName = "json", Group = "input")]
	public string SwaggerJson { get; set; }

	[Option('f', "file", SetName = "file", Group = "input")]
	public string SwaggerFilePath { get; set; }
}

when running exe without any args we have output:

 At least one option from group 'input' (u, url, j, json, f, file) is required.

  -u, --url       (Group: input) (Default: http://localhost:5002/swagger/current/swagger.json)

  -j, --json      (Group: input)

  -f, --file      (Group: input)

So, default value for url will be set only when exe invoked with exe -j "value" or exe -f "value". Which is not what I expect as user of program when see help message.

Also new Group feature does not prevent setting default value for "mutually exclusive" properties, it that by design?

@moh-hassan
Copy link
Collaborator

@sm-g
Both SetName and group are not allowed for the option in the same time. Use only SetName OR Group.

Removing SetName

internal class Options
{
	[Option('u', "url",  Group = "input", Default = "http://localhost:5002/swagger/current/swagger.json")]
	public string SwaggerUrl { get; set; }

	[Option('j', "json",  Group = "input")]
	public string SwaggerJson { get; set; }

	[Option('f', "file", Group = "input")]
	public string SwaggerFilePath { get; set; }
}

a new PR may Support Default Value for Group.

cc @hadzhiyski
It's nice if Group support Default value and firing error message when both SetName and Group are used in the same time.

@hadzhiyski
Copy link
Contributor Author

@moh-hassan, @sm-g I will submit a new PR for this.

@moh-hassan
Copy link
Collaborator

Thanks @hadzhiyski for the quick response and support.

@hadzhiyski
Copy link
Contributor Author

The new PR is #575

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants