picocli icon indicating copy to clipboard operation
picocli copied to clipboard

Add auto-configured CommandLineRunner

Open rupert-madden-abbott opened this issue 3 years ago • 1 comments

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:

  • Add the above class
  • Perhaps add a marker interface e.g. PicocliRunner, which is then implemented by the above. Then make the @Bean conditional on missing beans in order to allow users to override the implementation if they want.
  • Update any relevant documentation

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.

rupert-madden-abbott avatar Aug 26 '21 21:08 rupert-madden-abbott

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

good-vi avatar Oct 12 '23 06:10 good-vi