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

Need documentation for how to handle custom parameter validation #1147

Closed
LorenKeagle opened this issue Aug 17, 2020 · 6 comments
Closed

Need documentation for how to handle custom parameter validation #1147

LorenKeagle opened this issue Aug 17, 2020 · 6 comments

Comments

@LorenKeagle
Copy link

First of all, thanks for this tool. It is a huge improvement over Commons CLI!

However, I was expecting some of the custom option validation that I had written for CLI to be supported using PicoCLI.

I have an application that accepts several different types of input, i.e. XML files, log files, report names, report ids. These are each implemented as Options, so they can individually be collected in lists:

@option(name="--xml") List xmlFiles;
@option(name="--log") List logFiles;
@option(name="--report-name") List reportNames;
@option(name="--report-id") List reportIDs;

These are neither mutually exclusive, nor dependent. I just need to validate that at least one input is provided to the app, and print the usage/help output otherwise. I can't find a way to do this, and there are no examples on how to add this validation into my business logic.

So my questions are:

  1. Is there a way to flag these options together as a group to validate that at least one of them is provided on the command line?
  2. If not, how can I manually indicate that there is an input validation issue from within my application code? Should I throw a particular exception?
  3. How can I have PicoCLI automatically indicate in the usage string that one of the options is required?

A second, related issue is that each of the options in the example above are already part of their own ArgGroups (with validate=false), so that I can keep them organized with other related flags in the help usage. I do not believe that I can include an option in multiple ArgGroups (please correct me if I am wrong!). Wouldn't it be better to be able to provide a groupName="foo" parameter in the Options attribute to collect items together, so that Options validation can be separated from the organization in the help usage?

@remkop
Copy link
Owner

remkop commented Aug 18, 2020

Hi @LorenKeagle glad to hear you like picocli!

To answer your questions:

  1. Is there a way to flag these options together as a group to validate that at least one of them is provided on the command line?

Picocli currently does not have this feature. Applications will need to do custom validation in the business logic (see below).

  1. If not, how can I manually indicate that there is an input validation issue from within my application code? Should I throw a particular exception?

There are two ways to do this: throw a ParameterException, or print an error message, the usage help message and return a non-zero exit code. Throwing a ParameterException may be easiest. Your application code could look something like this:

@Command(name = "myapp", mixinStandardHelpOptions = true, version = "myapp 0.1")
class MyApp implements Runnable {
    @Option(name="--xml") List<File> xmlFiles;
    @Option(name="--log") List<File> logFiles;
    @Option(name="--report-name") List<String> reportNames;
    @Option(name="--report-id") List<int> reportIDs;

    @Spec private CommandSpec spec; // injected by picocli

    public static void main(String... args) {
        System.exit(new CommandLine(new MyApp()).execute(args));
    }

    public void run() {
        validate();
        // business logic here
    }

    private void validate() {
        if (missing(xmlFiles) && missing(logFiles) && missing(reportNames) && missing(reportIDs)) {
            throw new ParameterException(spec.commandLine(),
                    "Missing option: at least one of the '--xml', '--log', '--report-name', or '--report-id' options must be specified.");
        }
    }

    private void missing(List<?> list) {
        return list == null || list.isEmpty();
    }
}

Throwing a ParameterException has the additional advantage that the default error handler will print the error message in bold red so that it stands out to users.

  1. How can I have PicoCLI automatically indicate in the usage string that one of the options is required?

Picocli cannot do this automatically, so the question is how to do this manually. This is an interesting problem.
Many commands follow the convention to use [ and ] square brackets for optional elements in the synopsis of the command, and elements without square brackets are taken to be required elements. Picocli also surrounds a group of required elements with ( and ) round brackets. There is no convention (that I am aware of) to indicate that at least one element of a group is required. You could approximate this by expressing the synopsis like this:

<cmd> ( [--xml] [--log] [--report-name] [--report-id] )

... but I am not sure this expresses your intention very well.
The next best thing is to explain in the description of the command that "at least one of the --xml, --log, --report-name, or --report-id options must be specified", or explain something similar in the description for each of these options.

I do not believe that I can include an option in multiple ArgGroups

That is correct.

Wouldn't it be better to be able to provide a groupName="foo" parameter in the Options attribute to collect items together, so that Options validation can be separated from the organization in the help usage?

Yes, that is a different way to design things, with different trade-offs.
In its current design, picocli Argument Groups only provide automatic validation for mutually exclusive and mutually dependent options, and repeating combinations of such groups, up to any level of depth. This last aspect is why I ended up modeling picocli's Argument Groups as a class: each repetition can be captured in an instance of the class associated with the group.
I believe this is a unique and very powerful feature that no other CLI library has. There are trade-offs with this design, and one of these trade-offs is that not all relationships between options can be modeled using picocli Argument Groups. In some cases (like yours), the application will need to provide custom validation logic.

@LorenKeagle
Copy link
Author

Thank you for the response.

The example of how to use the combination of the ParameterException and CommandSpec is exactly what I was looking for.

As for the ArgGroup discussion, I believe the design is good from the perspective of exclusive and dependent option groups. My concern was more regarding the organization of options in the usage text, as opposed to the validation functionality. While I may want to be able to express the relationships between options in terms of mutual exclusivity and dependency, I may also want to organize options in the help text according to their related functionality. I suppose in many cases, subcommands could be a natural mechanism to isolate things, but that doesn't fit my use case. In the meantime, I have different option sets for different vendor interoperability that I want to have grouped in the help text, but some of those options need to have their own mutual dependencies with other global options.

I'll work my way up to learning how to organize subcommands eventually, but it would take some thinking to redesign the tool entirely around that concept. I'm just getting started on this port from Commons CLI.

@remkop
Copy link
Owner

remkop commented Aug 18, 2020

I see what you mean now, thanks for the clarification!
Basically, you may have one grouping from a validation point of view and a different grouping from how you want to present the options in the usage help. Yes, picocli does not offer a convenient annotation-based way to do this.

It is possible to customize the options list programmatically using the Help API, see this example and this example but this is quite a bit more complicated than the annotations-based approach with Argument Groups.

If my understanding is correct your question has been answered, so if it is okay with you I will close this ticket.
Feel free to open new tickets whenever new questions come up.
Enjoy using picocli!

@remkop remkop closed this as completed Aug 18, 2020
@remkop remkop added this to the 4.6 milestone Aug 18, 2020
@remkop
Copy link
Owner

remkop commented Aug 18, 2020

Reopening: let's take this opportunity to add a section about Validation to the user manual.

@remkop remkop reopened this Aug 18, 2020
@remkop remkop closed this as completed in d556672 Aug 26, 2020
@remkop remkop modified the milestones: 4.6, 4.5.1 Aug 26, 2020
@remkop
Copy link
Owner

remkop commented Aug 26, 2020

I added a section on on Validation to the user manual.

@remkop
Copy link
Owner

remkop commented Aug 26, 2020

Picocli 4.5.1 has been released, with the updated user manual.

Don't forget to star ⭐ picocli on GitHub if you like the project! 😉

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

No branches or pull requests

2 participants