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

Mixin-with-prefix capability? #2310

Open
alexturc opened this issue Jun 25, 2024 Discussed in #1808 · 4 comments
Open

Mixin-with-prefix capability? #2310

alexturc opened this issue Jun 25, 2024 Discussed in #1808 · 4 comments

Comments

@alexturc
Copy link

Discussed in #1808

Originally posted by rcauble September 11, 2022
Hi,

I have a use case where I have 2 commands with each their own set of settings:

class Command1Settings {
    @CommandLine.Option(names = "--foo")
    private String foo;
    @CommandLine.Option(names = "--bar")
    private String bar;
}

@CommandLine.Command(name = "command1")
public static final class Command1  {
    @CommandLine.Mixin
    private Command1Settings settings = new Command1Settings();

    public void run() {
        String results1 = Command1Runner.run(settings);
        System.out.println(results1);
    }
}

class Command2Settings {
    @CommandLine.Option(names = "--foo")
    private String foo;
    @CommandLine.Option(names = "--bar")
    private String bar;
}

@CommandLine.Command(name = "command2")
 public static final class Command2  {
    @CommandLine.Mixin
    private Command2Settings settings = new Command2Settings();
    public void run() {
        String results2 = Command2Runner.run(settings);
         System.out.println(results2);
    }
}

And I am trying to create a 3rd command that computes a diff of the other 2. As you can see above, though there is overlap in the options of the 2 commands and so I'm looking for a MixinWithPrefix capability so that I can do something like this:

@CommandLine.Command(name = "diff")
public static final class Diff  {
    @CommandLine.MixinWithPrefix("first-")
    private Command1Settings settings1 = new Command1Settings();
    @CommandLine.MixinWithPrefix("second-")
    private Command1Settings settings2 = new Command2Settings();
    public void run() {
        String results1 = Command1Runner.run(settings1);
        String results2 = Command2Runner.run(settings2);
         System.out.println(diff(results1, results2));
    }
}

And then this will define a new command where the user can do:

diff --first-foo thing1 --first-bar thing2 --second-foo thing3 --second-bar thing4

To add an additional constraint for context, Command1 and Command2 live in different libraries from the differ and so I can't simply arrange for the names to be distinct as they are not necessarily owned by me.

Any suggestions?

alexturc pushed a commit to alexturc/picocli that referenced this issue Jun 25, 2024
alexturc pushed a commit to alexturc/picocli that referenced this issue Jun 25, 2024
…ixin reuse in the same command.

Addresses issue remkop#2310

	# Please enter the commit message for your changes. Lines starting
@remkop
Copy link
Owner

remkop commented Jul 12, 2024

@alexturc I see the PR for this feature (#2311) but to be honest I don't like the idea...

Note that there is already a generic mechanism to transform the model:
https://picocli.info/#_model_transformations

If that mechanism has limitations so that it cannot be used for this use case I would rather improve the existing generic mechanism rather than introduce another more narrow new mechanism.

@alexturc
Copy link
Author

Hi @remkop,

Mixins are a great way of grouping related options such that complex commands can be built while still being able to manage complexity. But a feature which I'm facing quite often and became a burden is inability of reuse mixins in the same command, as option names would clash.

A typical example is for database connections (but I encounter multiple such situations), where a mixin would be

public class DbMixin {
  @Option(name = "--url")
  String url;
  @Option(name = "--user")
  String user;
  @Option(name = "--pass")
  String pass;
}

An application working with multiple DB connections would reuse the above class like this:

@Command
public class DbMixin {
  @Mixin
  DbMixin firstDbMixin = new DbMixin();
  @Mixin
  DbMixin secondDbMixin = new DbMixin();
  @Mixin
  DbMixin thirdDbMixin = new DbMixin();
}

As of now this would not work as option names coming from different DbMixin instances would class an building the command line would fail. And it would fail before having the chance to apply model transformations as you suggested - because the are applied last, after an initial version of the model was build.

The goal here would be to add a prefix to option names originating from the three mixins such that we can successfully build a command line while reusing DbMxin class. --url option from firstDbMixin could become --firstDb.url or --firstDbUrl etc.

The solution int he PR keeps the mapping logic at mixin declaration, so I think it has good developer ergonomics - one can see immediately how option names are transformed.

But I can work with IModelTransformer as well if you would be ok to have @Mixin annotation take a model transformer and apply it immediately after the mixin is built.
In practice I intend the have a transformer which would take the name of the mixin and and use it as a prefix for option names.

Would this be acceptable?
If yes, would you prefer for @Mixin to take an array of transformers or just one, like @Command does?

@alexturc
Copy link
Author

I sent another PR which uses IModelTransformer with mixins. This alows me to make more changes actually, including prefixing the environment variables from where the default values would be taken by default. Please let me know if this acceptable.

@remkop
Copy link
Owner

remkop commented Aug 16, 2024 via email

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

No branches or pull requests

2 participants