Выбор необходимых параметров Picocli на основе основного варианта - PullRequest
1 голос
/ 07 мая 2020

Я хотел бы проанализировать параметры с помощью picocli в следующем формате:

application -mode CLIENT -c aaaa -d bbbb
application -mode SERVER -e xxxx -f yyyy

mode - это enum со значениями { CLIENT, SERVER }

  • If * 1010 Параметры *, -c и -d являются обязательными, а -e, -f использовать нельзя.
  • Если параметры mode == SERVER, -e и -f являются обязательными, и -c, -d использовать нельзя.

Другими словами, я хотел бы выбрать требуемые параметры на основе ключевого параметра. Возможно ли это в пикокли?

1 Ответ

1 голос
/ 08 мая 2020

Да, это возможно. Один из способов - это простая программная c проверка:

import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.ParameterException;
import picocli.CommandLine.Spec;

import java.util.Objects;
import java.util.function.Predicate;

@Command(name = "application", mixinStandardHelpOptions = true)
public class MyApp implements Runnable {

    enum Mode {CLIENT, SERVER}

    @Option(names = "-mode", required = true)
    Mode mode;

    @Option(names = "-c") String c;
    @Option(names = "-d") String d;
    @Option(names = "-e") String e;
    @Option(names = "-f") String f;

    @Spec CommandSpec spec;

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

    @Override
    public void run() {
        validateInput();
        // business logic here...
    }

    private void validateInput() {
        String INVALID = "Error: option(s) %s cannot be used in %s mode";
        String REQUIRED = "Error: option(s) %s are required in %s mode";
        if (mode == Mode.CLIENT) {
            check(INVALID, "CLIENT", Objects::isNull, e, "-e", f, "-f");
            check(REQUIRED, "CLIENT", Objects::nonNull, c, "-c", d, "-d");
        } else if (mode == Mode.SERVER) {
            check(INVALID, "SERVER", Objects::isNull, c, "-c", d, "-d");
            check(REQUIRED, "SERVER", Objects::nonNull, e, "-e", f, "-f");
        }
    }

    private void check(String msg, String param, Predicate<String> validator, String... valuesAndLabels) {
        String desc = "";
        String sep = "";
        for (int i = 0; i < valuesAndLabels.length; i += 2) {
            if (validator.test(valuesAndLabels[i])) {
                desc = sep + valuesAndLabels[i + 1];
                sep = ", ";
            }
        }
        if (desc.length() > 0) {
            throw new ParameterException(spec.commandLine(), String.format(msg, desc, param));
        }
    }
}

В качестве альтернативы, если вы хотите немного изменить свои требования, мы можем использовать группы аргументов picocli для более декларативного подхода. :

import picocli.CommandLine;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

@Command(name = "application", mixinStandardHelpOptions = true)
public class MyApp2 implements Runnable {

    static class ClientArgs {
        @Option(names = "-clientMode", required = true) boolean clientMode;
        @Option(names = "-c", required = true) String c;
        @Option(names = "-d", required = true) String d;
    }

    static class ServerArgs {
        @Option(names = "-serverMode", required = true) boolean serverMode;
        @Option(names = "-e", required = true) String e;
        @Option(names = "-f", required = true) String f;
    }

    static class Args {
        @ArgGroup(exclusive = false, multiplicity = "1", heading = "CLIENT mode args%n")
        ClientArgs clientArgs;

        @ArgGroup(exclusive = false, multiplicity = "1", heading = "SERVER mode args%n")
        ServerArgs serverArgs;
    }

    @ArgGroup(exclusive = true, multiplicity = "1")
    Args args;

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

    @Override
    public void run() {
        // business logic here...
    }
}

При вызове только с -serverMode этот второй пример покажет это сообщение об ошибке, за которым следует справочное сообщение:

Error: Missing required argument(s): -e=<e>, -f=<f>
Usage: application ((-clientMode -c=<c> -d=<d>) | (-serverMode -e=<e> -f=<f>))
...

Обратите внимание, что этот декларативный подход не может быть достигается с помощью одной опции -mode: каждой группе аргументов нужен свой вариант (в этом примере я выбрал -clientMode и -serverMode). Это позволяет синтаксическому анализатору picocli определять, какие опции должны встречаться вместе, а какие являются взаимоисключающими.

...