CLI с Picocli: вызов основной команды до вызова подкоманды - PullRequest
0 голосов
/ 08 мая 2018

Я перешел с Apache Commons CLI на Picocli из-за поддержки подкоманд (и объявления на основе аннотаций).

Рассмотрим инструмент командной строки, например git, с подкомандами, например push. У Git есть главный переключатель --verbose или -v для включения подробного режима в всех подкомандах . Как я могу реализовать главный переключатель, который выполняется перед любыми подкомандами?

Это мой тест

@CommandLine.Command(name = "push",
        description = "Update remote refs along with associated objects")
class PushCommand implements Callable<Void> {
    @Override
    public Void call() throws Exception {
        System.out.println("#PushCommand.call");

        return null;
    }
}

@CommandLine.Command(description = "Version control", subcommands = {PushCommand.class})
public class GitApp implements Callable<Void> {
    @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "Display this help message.")
    private boolean usageHelpRequested;

    @CommandLine.Option(names = {"-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting.")
    private boolean verboseMode;

    public static void main(String[] args) {
        GitApp app = new GitApp();
        CommandLine.call(app, "--verbose", "push");
        System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
    }

    @Override
    public Void call() throws Exception {
        System.out.println("#GitApp.call");

        return null;
    }
}

Вывод

#PushCommand.call
#GitApp.main after. verbose: true

Я ожидаю, что GitApp.call будет вызван до вызова подкоманды. Но вызывается только подкоманда.

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

Методы CommandLine.callCommandLine.run) вызывают только подкоманду last , так что то, что вы видите в исходном сообщении, является ожидаемым поведением.

Методы call и run на самом деле являются ярлыком.Следующие две строки эквивалентны:

CommandLine.run(callable, args); // internally uses RunLast, equivalent to: 
new CommandLine(callable).parseWithHandler(new RunLast(), args);

Существует также обработчик RunAll , который запускает все соответствующие команды.Следующий метод main дает желаемое поведение:

public static void main(String[] args) {
    args = new String[] { "--verbose", "push" };
    GitApp app = new GitApp();
    new CommandLine(app).parseWithHandler(new RunAll(), args);
    System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
}

Вывод:

#GitApp.call
#PushCommand.call
#GitApp.main after. verbose: true

Вас также может заинтересовать аннотация @ParentCommand.Это говорит Picocli о необходимости вставки экземпляра родительской команды в подкоманду.Ваша подкоманда может затем вызвать методы родительской команды, например, чтобы проверить, является ли verbose истинным.Например:

import picocli.CommandLine;
import picocli.CommandLine.*;

@Command(name = "push",
        description = "Update remote refs along with associated objects")
class PushCommand implements Runnable {

    @ParentCommand // picocli injects the parent instance
    private GitApp parentCommand;

    public void run() {
        System.out.printf("#PushCommand.call: parent.verbose=%s%n",
                parentCommand.verboseMode); // use parent instance
    }
}

@Command(description = "Version control",
        mixinStandardHelpOptions = true, // auto-include --help and --version
        subcommands = {PushCommand.class,
                       HelpCommand.class}) // built-in help subcommand
public class GitApp implements Runnable {
    @Option(names = {"-v", "--verbose"},
            description = "Verbose mode. Helpful for troubleshooting.")
    boolean verboseMode;

    public void run() {
        System.out.println("#GitApp.call");
    }

    public static void main(String[] args) {
        args = new String[] { "--verbose", "push" };
        GitApp app = new GitApp();
        new CommandLine(app).parseWithHandler(new RunAll(), args);
        System.out.println("#GitApp.main after. verbose: " + (app.verboseMode));
    }
}

Другие незначительные правки: сделали аннотации немного более компактными, импортировав внутренние классы.Вам также может понравиться атрибут mixinStandardHelpOptions и встроенная подкоманда help, которая помогает уменьшить стандартный код.

0 голосов
/ 11 мая 2018

Поскольку Picocli поддерживает наследование с помощью опций, я извлек опцию --help и --verbose в абстрактный класс BaseCommand и вызывал super.call из подкоманд.

abstract class BaseCommand implements Callable<Void> {
    @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "Display this help message.")
    private boolean usageHelpRequested;

    @CommandLine.Option(names = {"-v", "--verbose"}, description = "Verbose mode. Helpful for troubleshooting.")
    private boolean verboseMode;

    @Override
    public Void call() throws Exception {
        if (verboseMode) {
            setVerbose();
        }
        return null;
    }

    private void setVerbose() {
        System.out.println("enter verbose mode");
    }
}

@CommandLine.Command(name = "push",
        description = "Update remote refs along with associated objects")
class PushCommand extends BaseCommand {
    @Override
    public Void call() throws Exception {
        super.call();
        System.out.println("Execute push command");
        return null;
    }
}

@CommandLine.Command(description = "Version control", subcommands = {PushCommand.class})
public class GitApp extends BaseCommand {
    public static void main(String[] args) {
        GitApp app = new GitApp();
        CommandLine.call(app, "push", "--verbose");
    }

    @Override
    public Void call() throws Exception {
        super.call();
        System.out.println("GitApp.call called");
        return null;
    }
}
...