Search code examples
picocli

picocli : dependent arguments in ArgGroup


My question is similar, and simpler, to the one asked here.

I have three options, -A, -A1, -A2 (conceptually, part of a single group). The relationships required are these:

  1. Neither of these are required
  2. -A should be given along with at least one of -A1 or -A2
  3. Both -A1 and -A2 can be given with a single -A

In other words:

  • Valid specifications: -A -A1, -A -A2, and -A -A1 -A2
  • Invalid specifications: -A, -A1, -A2, and -A1 -A2

This is what I have using two @ArgGroups:

import picocli.CommandLine;
import picocli.CommandLine.*;
import picocli.CommandLine.Model.CommandSpec;

public class App implements Runnable {

    static class MyGroupX {
        @Option(names="-A1", required=false) boolean A1;
        @Option(names="-A2", required=false) boolean A2;
    }

    static class MyGroup {
        @Option(names="-A", required=true) boolean A;
        @ArgGroup(exclusive=false, multiplicity="1") MyGroupX myGroupX;
    }

    @ArgGroup(exclusive=false) MyGroup myGroup;

    @Spec CommandSpec spec;

    @Override
    public void run() {
        System.out.printf("OK: %s%n", spec.commandLine().getParseResult().originalArgs());
    }

    public static void main(String[] args) {
        //test: these should be valid
        new CommandLine(new App()).execute();
        new CommandLine(new App()).execute("-A -A1".split(" "));
        new CommandLine(new App()).execute("-A -A2".split(" "));
        new CommandLine(new App()).execute("-A -A1 -A2".split(" "));

        //test: these should FAIL
        new CommandLine(new App()).execute("-A");
        new CommandLine(new App()).execute("-A1");
        new CommandLine(new App()).execute("-A2");
        new CommandLine(new App()).execute("-A1 -A2".split(" "));
    }
}

Is there a simpler way?

Thanks!


Solution

  • Some relationships cannot be expressed with the @ArgGroup annotations only, and in that case custom validation logic in the application is needed.

    However, this is not the case for your application. It appears to me that you have found a very compact way to express your requirements.

    The valid user input sequences -A -A1, -A -A2, and -A -A1 -A2 are all accepted.

    The invalid user input sequences are all rejected with a reasonably clear error message:

    Input     Error message
    -----     -------------
    -A        Missing required argument(s): ([-A1] [-A2])
    -A1       Missing required argument(s): -A
    -A2       Missing required argument(s): -A
    -A1 -A2   Missing required argument(s): -A
    

    The application achieved all this without any explicit validation logic in the code, it was all done declaratively with annotations. Mission accomplished, I would say. I don't see a way to improve on this further.