Revisit positional arguments
- Add better mapping logic - Add better type conversion - More docs for arity and positional option configuration - Fixes #616
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -269,7 +269,10 @@ public interface CommandParser {
|
||||
// don't do anything if first arg looks like an option as if we are here
|
||||
// then we'd might remove wrong required option
|
||||
if (!oargs.isEmpty() && !oargs.get(0).startsWith("-")) {
|
||||
results.add(new DefaultCommandParserResult(o, oargs.stream().collect(Collectors.joining(" "))));
|
||||
// as we now have a candicate option, try to see if there is a
|
||||
// conversion we can do and the use it.
|
||||
Object value = convertOptionType(o, oargs);
|
||||
results.add(new DefaultCommandParserResult(o, value));
|
||||
requiredOptions.remove(o);
|
||||
}
|
||||
});
|
||||
@@ -285,6 +288,15 @@ public interface CommandParser {
|
||||
return new DefaultCommandParserResults(results, positional, errors);
|
||||
}
|
||||
|
||||
private Object convertOptionType(CommandOption option, Object value) {
|
||||
if (conversionService != null && option.getType() != null && value != null) {
|
||||
if (conversionService.canConvert(value.getClass(), option.getType().getRawClass())) {
|
||||
value = conversionService.convert(value, option.getType().getRawClass());
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static class ParserResult {
|
||||
private CommandOption option;
|
||||
private List<String> args;
|
||||
@@ -335,12 +347,7 @@ public interface CommandParser {
|
||||
if (holder.error != null) {
|
||||
return Stream.of(ParserResult.of(o, subArgs, null, holder.error));
|
||||
}
|
||||
Object value = holder.value;
|
||||
if (conversionService != null && o.getType() != null && value != null) {
|
||||
if (conversionService.canConvert(value.getClass(), o.getType().getRawClass())) {
|
||||
value = conversionService.convert(value, o.getType().getRawClass());
|
||||
}
|
||||
}
|
||||
Object value = convertOptionType(o, holder.value);
|
||||
Stream<ParserResult> unmapped = holder.unmapped.stream()
|
||||
.map(um -> ParserResult.of(null, Arrays.asList(um), null, null));
|
||||
Stream<ParserResult> res = Stream.of(ParserResult.of(o, subArgs, value, null));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -60,6 +60,8 @@ public abstract class AbstractCommandTests {
|
||||
public int method7Arg3;
|
||||
public int method8Count;
|
||||
public float[] method8Arg1;
|
||||
public int method9Count;
|
||||
public String[] method9Arg1;
|
||||
|
||||
public void method1(CommandContext ctx) {
|
||||
method1Ctx = ctx;
|
||||
@@ -107,5 +109,10 @@ public abstract class AbstractCommandTests {
|
||||
method8Arg1 = arg1;
|
||||
method8Count++;
|
||||
}
|
||||
|
||||
public void method9(String[] arg1) {
|
||||
method9Arg1 = arg1;
|
||||
method9Count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -195,7 +195,47 @@ public class CommandExecutionTests extends AbstractCommandTests {
|
||||
.build();
|
||||
execution.evaluate(r1, new String[]{"myarg1value1", "myarg1value2"});
|
||||
assertThat(pojo1.method4Count).isEqualTo(1);
|
||||
assertThat(pojo1.method4Arg1).isEqualTo("myarg1value1 myarg1value2");
|
||||
assertThat(pojo1.method4Arg1).isEqualTo("myarg1value1,myarg1value2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodMultiPositionalArgsAllToArray1() {
|
||||
CommandRegistration r1 = CommandRegistration.builder()
|
||||
.command("command1")
|
||||
.description("help")
|
||||
.withOption()
|
||||
.longNames("arg1")
|
||||
.description("some arg1")
|
||||
.position(0)
|
||||
.arity(OptionArity.ONE_OR_MORE)
|
||||
.and()
|
||||
.withTarget()
|
||||
.method(pojo1, "method9")
|
||||
.and()
|
||||
.build();
|
||||
execution.evaluate(r1, new String[]{"myarg1value1", "myarg1value2"});
|
||||
assertThat(pojo1.method9Count).isEqualTo(1);
|
||||
assertThat(pojo1.method9Arg1).isEqualTo(new String[] { "myarg1value1", "myarg1value2" });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodMultiPositionalArgsAllToArray2() {
|
||||
CommandRegistration r1 = CommandRegistration.builder()
|
||||
.command("command1")
|
||||
.description("help")
|
||||
.withOption()
|
||||
.longNames("arg1")
|
||||
.description("some arg1")
|
||||
.position(0)
|
||||
.arity(OptionArity.ONE_OR_MORE)
|
||||
.and()
|
||||
.withTarget()
|
||||
.method(pojo1, "method8")
|
||||
.and()
|
||||
.build();
|
||||
execution.evaluate(r1, new String[]{"1", "2"});
|
||||
assertThat(pojo1.method8Count).isEqualTo(1);
|
||||
assertThat(pojo1.method8Arg1).isEqualTo(new float[] { 1, 2 });
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -198,7 +198,8 @@ public class CommandParserTests extends AbstractCommandTests {
|
||||
CommandParserResults results = parser.parse(options, args);
|
||||
assertThat(results.results()).hasSize(1);
|
||||
assertThat(results.results().get(0).option()).isSameAs(option1);
|
||||
assertThat(results.results().get(0).value()).isEqualTo("value foo");
|
||||
// no type so we get raw list
|
||||
assertThat(results.results().get(0).value()).isEqualTo(Arrays.asList("value", "foo"));
|
||||
assertThat(results.positional()).containsExactly("value", "foo");
|
||||
}
|
||||
|
||||
@@ -210,7 +211,7 @@ public class CommandParserTests extends AbstractCommandTests {
|
||||
CommandParserResults results = parser.parse(options, args);
|
||||
assertThat(results.results()).hasSize(1);
|
||||
assertThat(results.results().get(0).option()).isSameAs(option1);
|
||||
assertThat(results.results().get(0).value()).isEqualTo("value foo");
|
||||
assertThat(results.results().get(0).value()).isEqualTo("value,foo");
|
||||
assertThat(results.positional()).containsExactly("value", "foo");
|
||||
}
|
||||
|
||||
@@ -331,10 +332,35 @@ public class CommandParserTests extends AbstractCommandTests {
|
||||
assertThat(results2.results().get(0).value()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapToIntArray() {
|
||||
CommandOption option1 = CommandOption.of(
|
||||
new String[] { "arg1" },
|
||||
null,
|
||||
null,
|
||||
ResolvableType.forType(int[].class),
|
||||
false,
|
||||
null,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
null,
|
||||
null);
|
||||
|
||||
|
||||
List<CommandOption> options = Arrays.asList(option1);
|
||||
String[] args = new String[]{"1", "2"};
|
||||
CommandParserResults results = parser.parse(options, args);
|
||||
assertThat(results.errors()).hasSize(0);
|
||||
assertThat(results.results()).hasSize(1);
|
||||
assertThat(results.results().get(0).option()).isSameAs(option1);
|
||||
assertThat(results.results().get(0).value()).isEqualTo(new int[] { 1, 2 });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapPositionalArgs1() {
|
||||
CommandOption option1 = longOption("arg1", 0, 1, 1);
|
||||
CommandOption option2 = longOption("arg2", 1, 1, 2);
|
||||
CommandOption option1 = longOption("arg1", ResolvableType.forType(String.class), false, 0, 1, 1);
|
||||
CommandOption option2 = longOption("arg2", ResolvableType.forType(String.class), false, 1, 1, 2);
|
||||
List<CommandOption> options = Arrays.asList(option1, option2);
|
||||
String[] args = new String[]{"--arg1", "1", "2"};
|
||||
CommandParserResults results = parser.parse(options, args);
|
||||
@@ -346,9 +372,25 @@ public class CommandParserTests extends AbstractCommandTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapPositionalArgs2() {
|
||||
public void testMapPositionalArgs11() {
|
||||
CommandOption option1 = longOption("arg1", 0, 1, 1);
|
||||
CommandOption option2 = longOption("arg2", 1, 1, 2);
|
||||
List<CommandOption> options = Arrays.asList(option1, option2);
|
||||
String[] args = new String[]{"--arg1", "1", "2"};
|
||||
CommandParserResults results = parser.parse(options, args);
|
||||
assertThat(results.results()).hasSize(2);
|
||||
assertThat(results.results().get(0).option()).isSameAs(option1);
|
||||
assertThat(results.results().get(1).option()).isSameAs(option2);
|
||||
assertThat(results.results().get(0).value()).isEqualTo("1");
|
||||
// no type so we get raw list
|
||||
assertThat(results.results().get(1).value()).isEqualTo(Arrays.asList("2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapPositionalArgs2() {
|
||||
CommandOption option1 = longOption("arg1", ResolvableType.forType(String.class), false, 0, 1, 1);
|
||||
CommandOption option2 = longOption("arg2", ResolvableType.forType(String.class), false, 1, 1, 2);
|
||||
|
||||
List<CommandOption> options = Arrays.asList(option1, option2);
|
||||
String[] args = new String[]{"1", "2"};
|
||||
CommandParserResults results = parser.parse(options, args);
|
||||
|
||||
Reference in New Issue
Block a user