remkop / picocli

Picocli is a modern framework for building powerful, user-friendly, GraalVM-enabled command line apps with ease. It supports colors, autocompletion, subcommands, and more. In 1 source file so apps can include as source & avoid adding a dependency. Written in Java, usable from Groovy, Kotlin, Scala, etc.
https://picocli.info
Apache License 2.0
4.93k stars 424 forks source link

Add auto-configured CommandLineRunner #1417

Open rupert-madden-abbott opened 3 years ago

rupert-madden-abbott commented 3 years ago

At the moment, the Spring Boot starter requires you to write some boilerplate to identify your "main" command in the form of a CommandLineRunner.

I always feel a bit sad when I had to add that into each new application I write on top of Picocli so I've been thinking of ways to remove that boilerplate and have come up with the following:

public class PicocliApplicationRunner implements CommandLineRunner, ExitCodeGenerator {

    private final ApplicationContext context;

    private final IFactory factory;

    private int exitCode;

    public PicocliApplicationRunner(ApplicationContext context, IFactory factory) {
        this.context = context;
        this.factory = factory;
    }

    @Override
    public void run(String... args) {
        Collection<Object> commands = context.getBeansWithAnnotation(Command.class).values();

        Set<Class<?>> subCommandClasses = commands.stream()
                .map(Object::getClass)
                .map(c -> c.getAnnotation(Command.class))
                .map(Command::subcommands)
                .flatMap(Arrays::stream)
                .collect(Collectors.toSet());

        List<Object> mainCommands = commands.stream()
                .filter(c -> !subCommandClasses.contains(c.getClass()))
                .collect(Collectors.toList());

        if (mainCommands.size() < 1) {
            throw new CommandLine.InitializationException("No main command bean can be found");
        } else if (mainCommands.size() > 1) {
            throw new CommandLine.InitializationException("Multiple main command beans found");
        }

        exitCode = new CommandLine(mainCommands.get(0), factory).execute(args);
    }

    @Override
    public int getExitCode() {
        return exitCode;
    }
}

What do you think? I am happy to raise a PR if you like this idea to do at least the following:

I've tested and this works fine in at least simple cases but if there are any more advanced setups that you think this might not work for, please let me know.

good-vi commented 1 year ago

Nice work. But picocli can't normally work with spring conditionals)