Reputation: 2148
I have a situation where I need to have three mandatory arguments (field1
, field2
and field3
. I then want the user to enter a command name
(mandatory, values can be create
, list
, etc). The command name must be entered, and must be singular (only one of them can be entered).
Some of the commands will have arguments, some of them will not. How do I handle that?
I tried the following, but I get an error:
ArgGroup has no options or positional parameters, and no subgroups
public class CliParserArgs {
@Option(names = {"--field1"}, required = true)
private String field1;
@Option(names = {"--field2"}, required = true)
String field2;
@Option(names={"--field3"}, required = true)
String field3;
@Option(names = {"-h", "--help"}, usageHelp = true) boolean help;
class Create {
private final String val;
public Create(final String val) {
this.val = val;
}
}
class ListObjects {
private final String val;
public ListObjects(final String val) {
this.val = val;
}
}
@ArgGroup(heading = "Command", exclusive = true, multiplicity = "1")
Create create;
ListObjects listObjects;
public static void main(String[] args) {
CliParserArgs cliParserArgs = new CliParserArgs();
CommandLine cmd = new CommandLine(cliParserArgs);
CommandLine.ParseResult parseResult = cmd.parseArgs(args);
System.err.println("parse results: " + parseResult.matchedArgs().toString());
try {
if (cmd.isUsageHelpRequested()) {
cmd.usage(System.out);
}
} catch (CommandLine.ParameterException e) {
System.err.println("error: " + e.getMessage());
System.err.println(e.getStackTrace());
}
}
}
Upvotes: 2
Views: 1119
Reputation: 36754
It sounds like you want to create a command with subcommands. You can do this in picocli by either marking a method with the @Command
annotation or by creating a separate command class and registering it as a subcommand of your parent command. If your subcommand has many options, you probably want to create a separate class for it.
Once you have created subcommands, you want to call the logic for the subcommand that the user specified. You can do this manually, using the CommandLine.parseArgs
method, but this is a lot of work. I would recommend using the CommandLine.execute
method instead.
The execute
method will parse the user input, handle --help
and --version
requests, handle invalid user input, and finally (if the user input was valid) invoke the business logic of the subcommand that the user specified. It will also return an exit code.
The execute
method requires subcommands to be either a @Command
-annotated method or a @Command
-annotated class that implements Runnable
or Callable
.
Below is an example based on your example code, implemented as subcommands.
@Command(name = "cli", version = "1.0",
mixinStandardHelpOptions = true,
subcommands = {Create.class, ListObjects.class})
public class Cli implements Runnable {
@Option(names = {"--field1"}, required = true)
private String field1;
@Option(names = {"--field2"}, required = true)
String field2;
@Option(names={"--field3"}, required = true)
String field3;
// not needed because we have mixinStandardHelpOptions=true
//@Option(names = {"-h", "--help"}, usageHelp = true) boolean help;
public void run() {
// business logic of the top-level cmd here
System.out.println("hi, field1="+field1);
}
public static void main(String[] args) {
int exitCode = new CommandLine(new Cli()).execute(args);
System.exit(exitCode);
}
}
@Command(name = "create", description = "create ...",
mixinStandardHelpOptions = true, version = "1.0")
class Create implements Callable<Integer> {
@Option(names = {"-x", "--times"}, description = "...")
int x;
@Override
public Integer call() {
// business logic for "create" here...
return ok ? 0 : 1; // exit code support
}
}
@Command(name = "list", description = "create ...",
mixinStandardHelpOptions = true, version = "1.0")
class ListObjects implements Runnable {
@Option(names = {"-x", "--times"}, description = "...")
int x;
@Override
public void run() {
// business logic for "list" here...
}
}
Upvotes: 5