rvesse / airline

Java annotation-based framework for parsing Git like command line structures with deep extensibility
https://rvesse.github.io/airline/
Apache License 2.0
128 stars 20 forks source link

Unexpected param isn't thrown if there is an argument #112

Closed reevesg closed 2 years ago

reevesg commented 2 years ago
package com.drw.xd.tools.test;

import com.github.rvesse.airline.Cli;
import com.github.rvesse.airline.annotations.Arguments;
import com.github.rvesse.airline.annotations.Command;
import com.github.rvesse.airline.annotations.Option;

public class TestMain {
    public static void main(String[] args) throws Exception {
         Cli.<Runnable>builder("test")
                    .withDescription("Test the cli")
                    .withCommand(TestMain.ArgsCmd.class)
                    .build()
                    .parse(args)
                    .run();
    }

    @Command(name = "args", description = "Test args")
    public static class ArgsCmd implements Runnable {
        @Arguments(title = "arg")
        public String arg;
        @Option(name = {"-o"})
        public boolean option = false;

        public void run() {
            System.out.println("arg = " + arg);
            System.out.println("option = " + option);
        }
    }
}

produces output of

> test args --foo
arg = --foo
option = false

Is it possible to have arguments but still have unknown options throw an exception?

Thanks!

rvesse commented 2 years ago

There isn't currently.

In my $dayjob we have use cases where we want this behaviour, e.g. when building wrappers around other commands, and we legitimately want to be able to pass in things that look like options that are passed through to some underlying process.

That being said you could create a custom restriction to achieve this and I can see this as being a useful feature for other users. I'll put together a PR for this and provide you with a SNAPSHOT build you can test to see if my idea resolves your issue

rvesse commented 2 years ago

Please see the proposed new @NoOptionLikeValues annotation in PR #113

I have published a 2.8.4-SNAPSHOT build that has this proposed change if you wish to try it out, you'll need to point your build to the OSSRH Snapshot Repositories to pick this up

reevesg commented 2 years ago

Thanks for working on this @rvesse! I tried pulling a snapshot build but couldn't find it. I would expect to find a rvesse in https://s01.oss.sonatype.org/content/repositories/snapshots/com/github/ but don't.

Am I looking in the wrong place?

rvesse commented 2 years ago

@reevesg Looks like it's here - https://oss.sonatype.org/content/repositories/snapshots/com/github/rvesse/airline/

reevesg commented 2 years ago

@rvesse I tried the snapshot build and it works great. Thanks!

When do you think you will release 2.8.4?

rvesse commented 2 years ago

@reevesg Possibly next week

One outstanding question is whether it would be useful to be able to use @NoOptionLikeValues as a global restriction?

i.e. you could specify it at your Command/CLI class level and it would validate that no options/arguments received an option like value

This is as opposed to the current PR proposal whereby you have to manually add it to each @Option or @Arguments annotated field you want it to apply to.

Note that making it work as a global restriction would not preclude it still being applied directly to option/argument fields

reevesg commented 2 years ago

@rvesse sorry for the delay. Yeah I think the global restriction would be best in some cases, but its also really useful as it is.

rvesse commented 2 years ago

@reevesg I've just pushed out a 2.8.4 release today, this will take a few hours to be visible in Maven Central. Sorry it took so long to get around to this.

Note that I did end up making @NoOptionLikeValues be usable as a Global Restriction. In further testing I did discover an interesting gotcha with the restriction when applied to options/arguments that might legitimately have an option like prefix e.g. consider -1 passed to an Integer typed field.

When used directly on an Option/Argument then the restriction applies to the raw unparsed values and so would reject -1 unless you customised the annotation to use different option prefixes.

Whereas when used as a Global Restriction it applies over the parsed typed values and so can ignore anything that isn't a string since the parsing into the type likely already rejects bad values e.g. -typo would already be an invalid integer.

I've done my best to document this in both the Javadoc for the annotation and the corresponding User Guide page for the annotation. However hopefully this new annotation proves useful and resolves your issue.