Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Determine if help was requested using CommandLine in addition to command object itself #145

Closed
kakawait opened this issue Jun 23, 2017 · 11 comments

Comments

@kakawait
Copy link
Contributor

kakawait commented Jun 23, 2017

Do you think is possible to get something like:

public class PicocliCommandLineRunner implements CommandLineRunner {

    private final Object command;

    public PicocliCommandLineRunner(Object command) {
        this.command = command;
    }

    @Override
    public void run(String... args) throws Exception {
        CommandLine cli = new CommandLine(command);
        if (cli.isHelpRequested()) {
            CommandLine.usage(cli.getCommand(), System.out);
            return;
        }
    }
}

Since in my use case, command is injected so I can't control the type of my command!

@remkop
Copy link
Owner

remkop commented Jun 23, 2017

Would you be able to provide a pull request to clarify what you have in mind?

@remkop
Copy link
Owner

remkop commented Jun 24, 2017

I am a bit confused by this feature request to be honest. How would the CommandLine object know which field(s) should trigger the help message? For example, it is common for command line apps to have a --version option that is also annotated with @Option(help=true). (The help=true attribute value tells picocli to omit validation of missing required fields, it does not mean this field triggers the usage help. Confusing naming, sorry.)

Also, what injection framework do you use? Would it be possible for your domain objects to implement an interface that has the isHelpRequested method?

@kakawait
Copy link
Contributor Author

kakawait commented Jun 26, 2017

I think the main issue comes from

The help=true attribute value tells picocli to omit validation of missing required fields, it does not mean this field triggers the usage help. Confusing naming, sorry.

I'm currently trying to develop a Spring boot starter for Picocli which simplify and integrate Picocli inside CommandLineRunner.

Indeed, today in Spring boot if you want to do some stuff with CLI you can use CommandLineRunner or ApplicationRunner but both are not so great as Picocli.

That why I though created a Spring boot starter that will retrieve any beans with at least @Command, @Option or @Parameter annotations present and configure inside a CommandLineRunner.

For example, user will be allow to create such bean:

@Component
@Command("info")
class InfoCli implements Runnable {
    public void run() {
        System.out.println("Info message here");
    }
}

that will be automatically integrated inside a CommandLineRunner and thus when using its artifact like following

java -jar artifact_name_here.jar info

will execute run() method and thus printing out Info message here.

User can defined more complexe command, and multiple beans or even not a command like

class MainCli implements Runnable {
    @Option(names = {"-V", "--version"}, description = "display version info")
    boolean version;

    public void run() {
        if (version) {
            System.out.println("3.2.3");
        }
    }
}

Since I though help=true wanted to say

I want to display help usage

I was thinking to automatically call usage on CommandLineRunner like following

public class PicocliCommandLineRunner implements CommandLineRunner {

    private final PicocliCommand command;

    private final Set<PicocliSubCommand> subCommands;

    PicocliCommandLineRunner(PicocliCommand command, Set<PicocliSubCommand> subCommands) {
        this.command = command;
        this.subCommands = subCommands;
    }

    @Override
    public void run(String... args) throws Exception {
        CommandLine cli = new CommandLine(command);
        subCommands.forEach(s -> cli.addSubcommand(s.getSubCommandName(), s));
        try {
            cli.parse(args);
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
            cli.usage(System.err, Help.Ansi.ON);
            return;
        }
        if (isHelpRequested(command)) {
            cli.usage(System.out, Help.Ansi.ON);
            return;
        }
        Optional<Object> subCommand = cli.getSubcommands()
                .values()
                .stream()
                .map(CommandLine::getCommand)
                .filter(PicocliCommandLineRunner::isHelpRequested)
                .findFirst();
        if (subCommand.isPresent()) {
            CommandLine.usage(subCommand.get(), System.out);
            return;
        }
    }

    private static boolean isHelpRequested(Object object) {
        return object != null
                && Arrays.stream(AopUtils.getTargetClass(object).getDeclaredFields())
                         .filter(f -> f.isAnnotationPresent(Option.class))
                         .filter(f -> f.getAnnotation(Option.class).help())
                         .anyMatch(f -> {
                             try {
                                 return f.getBoolean(object);
                             } catch (IllegalAccessException e) {
                                 return false;
                             }
                         });
    }
}

But as you can see I have to scan every Object to lookup for help = true and I though that Picocli already did that, thus duplicate work...

@remkop
Copy link
Owner

remkop commented Jul 3, 2017

Taking another look at this since I've had an additional request for this feature.

Still thinking about how to implement this. I see 2 options:

  • change the meaning of the help = true attribute to automatically call usage. Introduce a new attribute (omitValidation?) for Version and other options where picocli should not check for required fields
  • introduce a new attribute (printHelp?) to automatically call usage. Keep existing help attribute to mean "don't check for required fields"

@kakawait
Copy link
Contributor Author

kakawait commented Jul 3, 2017

@remkop it only depends if you want to create a breaking change or not :)

About first option if think omitValidation = true should be default since when asking help we usually won't validate :)

@kakawait
Copy link
Contributor Author

kakawait commented Jul 6, 2017

Issue is related to my starter that can be now checkout on https://github.com/kakawait/picocli-spring-boot-starter

@remkop
Copy link
Owner

remkop commented Aug 5, 2017

I'm thinking to add usageHelp and versionHelp to the @Option annotation.
Also, thinking to add isUsageHelpRequested and isVersionHelpRequested to the CommandLine class. These return true if the corresponding option was specified on the command line.

@remkop remkop added this to the 0.9.8 milestone Aug 5, 2017
@remkop
Copy link
Owner

remkop commented Aug 5, 2017

By the way, I just wanted to say that I like your picocli Spring boot starter initiative very much!

@kakawait
Copy link
Contributor Author

kakawait commented Aug 5, 2017

@remkop But you still maintain help into @Option? If yes no problem but we need to write clear doc to explain difference between help and usageHelp.

And yes with versionHelp and both methods is*HelpRequested I will be me able to update Picocli Spring boot starter to remove some tricks I did to achieve that :)

@remkop
Copy link
Owner

remkop commented Aug 5, 2017

@kakawait Yes, help remains, with the same semantics as now.
Agree that documentation is important here. Please also take a look at related ticket #161.
I hope to be able to push the code for both to master this weekend.

@remkop remkop closed this as completed in 3918edd Aug 6, 2017
@remkop
Copy link
Owner

remkop commented Aug 6, 2017

Pushed the above changes to master, including changes to the user manual. Please review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants