Support explicit arity min max with @Option

- @Option now has arityMin/arityMax which if defined,
  non-negative, are used instead of arity.
- Fixes #731
This commit is contained in:
Janne Valkealahti
2023-05-01 08:36:41 +01:00
parent c36aa41878
commit 9d27466c6a
6 changed files with 225 additions and 40 deletions

View File

@@ -74,6 +74,28 @@ public @interface Option {
* Define option arity.
*
* @return option arity
* @see #arityMin()
* @see #arityMax()
*/
OptionArity arity() default OptionArity.NONE;
/**
* Define option arity min. If Defined non-negative will be used instead of
* {@link #arity()}. If {@code arityMax} is not set non-negative it is set to
* same as this.
*
* @return option arity min
* @see #arity()
*/
int arityMin() default -1;
/**
* Define option arity max. If Defined non-negative will be used instead of
* {@link #arity()}. If {@code arityMin} is not set non-negative it is set to
* zero.
*
* @return option arity max
* @see #arity()
*/
int arityMax() default -1;
}

View File

@@ -251,7 +251,22 @@ class CommandRegistrationFactoryBean implements FactoryBean<CommandRegistration>
optionSpec.shortNames(shortNames.toArray(new Character[0]));
optionSpec.position(mp.getParameterIndex());
optionSpec.description(so.description());
if (so.arity() != OptionArity.NONE) {
int arityMin = so.arityMin();
int arityMax = so.arityMax();
if (arityMin > -1) {
if (arityMax < arityMin) {
arityMax = arityMin;
}
}
else if (arityMax > -1) {
if (arityMin < 0) {
arityMin = 0;
}
}
if (arityMin > -1 && arityMax > -1) {
optionSpec.arity(arityMin, arityMax);
}
else if (so.arity() != OptionArity.NONE) {
optionSpec.arity(so.arity());
}
else {

View File

@@ -24,6 +24,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.shell.Availability;
import org.springframework.shell.AvailabilityProvider;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.CommandRegistration.OptionArity;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.CommandAvailability;
import org.springframework.shell.command.annotation.Option;
@@ -242,6 +243,78 @@ class CommandRegistrationFactoryBeanTests {
}
@Test
void setsOptionWithArity() {
configCommon(OptionWithArity.class, new OptionWithArity(), "command1", new Class[] { String.class })
.run((context) -> {
CommandRegistrationFactoryBean fb = context.getBean(FACTORYBEANREF,
CommandRegistrationFactoryBean.class);
assertThat(fb).isNotNull();
CommandRegistration registration = fb.getObject();
assertThat(registration).isNotNull();
assertThat(registration.getOptions().get(0).getArityMin()).isEqualTo(1);
assertThat(registration.getOptions().get(0).getArityMax()).isEqualTo(1);
});
configCommon(OptionWithArity.class, new OptionWithArity(), "command2", new Class[] { String.class })
.run((context) -> {
CommandRegistrationFactoryBean fb = context.getBean(FACTORYBEANREF,
CommandRegistrationFactoryBean.class);
assertThat(fb).isNotNull();
CommandRegistration registration = fb.getObject();
assertThat(registration).isNotNull();
assertThat(registration.getOptions().get(0).getArityMin()).isEqualTo(1);
assertThat(registration.getOptions().get(0).getArityMax()).isEqualTo(1);
});
configCommon(OptionWithArity.class, new OptionWithArity(), "command3", new Class[] { String.class })
.run((context) -> {
CommandRegistrationFactoryBean fb = context.getBean(FACTORYBEANREF,
CommandRegistrationFactoryBean.class);
assertThat(fb).isNotNull();
CommandRegistration registration = fb.getObject();
assertThat(registration).isNotNull();
assertThat(registration.getOptions().get(0).getArityMin()).isEqualTo(0);
assertThat(registration.getOptions().get(0).getArityMax()).isEqualTo(2);
});
configCommon(OptionWithArity.class, new OptionWithArity(), "command4", new Class[] { String.class })
.run((context) -> {
CommandRegistrationFactoryBean fb = context.getBean(FACTORYBEANREF,
CommandRegistrationFactoryBean.class);
assertThat(fb).isNotNull();
CommandRegistration registration = fb.getObject();
assertThat(registration).isNotNull();
assertThat(registration.getOptions().get(0).getArityMin()).isEqualTo(0);
assertThat(registration.getOptions().get(0).getArityMax()).isEqualTo(2);
});
}
@Command
private static class OptionWithArity {
@Command
void command1(@Option(longNames = "arg", arity = OptionArity.EXACTLY_ONE) String arg) {
}
@Command
void command2(@Option(longNames = "arg", arityMin = 1) String arg) {
}
@Command
void command3(@Option(longNames = "arg", arityMax = 2) String arg) {
}
@Command
void command4(@Option(longNames = "arg", arityMax = 2, arity = OptionArity.EXACTLY_ONE) String arg) {
}
@Bean
CompletionProvider completionProvider() {
return ctx -> {
return Collections.emptyList();
};
}
}
private <T> ApplicationContextRunner configCommon(Class<T> type, T bean) {
return configCommon(type, bean, "command", new Class[0]);
}