Handle arg looking options better

- Now lexing better with valid options
- Only report unrecognised option with double dash
  as current parser don't have structure to do
  deeper analysis.
- Fixes #651
This commit is contained in:
Janne Valkealahti
2023-02-01 09:29:20 +00:00
parent 3ce7afac6b
commit 809e296e55
2 changed files with 47 additions and 4 deletions

View File

@@ -22,6 +22,7 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Deque; import java.util.Deque;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -213,7 +214,15 @@ public interface CommandParser {
.filter(o -> o.isRequired()) .filter(o -> o.isRequired())
.collect(Collectors.toList()); .collect(Collectors.toList());
Lexer lexer = new Lexer(args); Set<String> splitValidValues = options.stream()
.flatMap(o -> {
Stream<String> longs = Stream.of(o.getLongNames()).map(l -> "--" + l);
Stream<String> shorts = Stream.of(o.getShortNames()).map(s -> "-"+ Character.toString(s));
return Stream.concat(longs, shorts);
})
.collect(Collectors.toSet());
Lexer lexer = new Lexer(args, splitValidValues);
List<List<String>> lexerResults = lexer.visit(); List<List<String>> lexerResults = lexer.visit();
Parser parser = new Parser(); Parser parser = new Parser();
ParserResults parserResults = parser.visit(lexerResults, options); ParserResults parserResults = parser.visit(lexerResults, options);
@@ -228,7 +237,7 @@ public interface CommandParser {
} }
else { else {
for (String arg : pr.args) { for (String arg : pr.args) {
if (arg.startsWith("-")) { if (arg.startsWith("--")) {
errors.add(UnrecognisedOptionException.of(String.format("Unrecognised option '%s'", arg), errors.add(UnrecognisedOptionException.of(String.format("Unrecognised option '%s'", arg),
arg)); arg));
} }
@@ -520,11 +529,13 @@ public interface CommandParser {
*/ */
private static class Lexer { private static class Lexer {
private final String[] args; private final String[] args;
Lexer(String[] args) { private final Set<String> splitValidValues;
Lexer(String[] args, Set<String> splitValues) {
this.args = args; this.args = args;
this.splitValidValues = splitValues;
} }
List<List<String>> visit() { List<List<String>> visit() {
return Utils.split(args, t -> t.startsWith("-")); return Utils.split(args, t -> splitValidValues.contains(t));
} }
} }
} }

View File

@@ -26,8 +26,10 @@ import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.shell.command.CommandParser.CommandParserResults; import org.springframework.shell.command.CommandParser.CommandParserResults;
import org.springframework.shell.command.CommandParser.MissingOptionException;
import org.springframework.shell.command.CommandParser.NotEnoughArgumentsOptionException; import org.springframework.shell.command.CommandParser.NotEnoughArgumentsOptionException;
import org.springframework.shell.command.CommandParser.TooManyArgumentsOptionException; import org.springframework.shell.command.CommandParser.TooManyArgumentsOptionException;
import org.springframework.shell.command.CommandParser.UnrecognisedOptionException;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@@ -496,6 +498,11 @@ public class CommandParserTests extends AbstractCommandTests {
CommandParserResults results = parser.parse(options, args); CommandParserResults results = parser.parse(options, args);
assertThat(results.results()).hasSize(0); assertThat(results.results()).hasSize(0);
assertThat(results.errors()).hasSize(1); assertThat(results.errors()).hasSize(1);
assertThat(results.errors()).satisfiesExactly(
e -> {
assertThat(e).isInstanceOf(UnrecognisedOptionException.class);
}
);
assertThat(results.positional()).hasSize(1); assertThat(results.positional()).hasSize(1);
} }
@@ -508,6 +515,11 @@ public class CommandParserTests extends AbstractCommandTests {
CommandParserResults results = parser.parse(options, args); CommandParserResults results = parser.parse(options, args);
assertThat(results.results()).hasSize(1); assertThat(results.results()).hasSize(1);
assertThat(results.errors()).hasSize(1); assertThat(results.errors()).hasSize(1);
assertThat(results.errors()).satisfiesExactly(
e -> {
assertThat(e).isInstanceOf(UnrecognisedOptionException.class);
}
);
assertThat(results.positional()).hasSize(1); assertThat(results.positional()).hasSize(1);
} }
@@ -520,9 +532,29 @@ public class CommandParserTests extends AbstractCommandTests {
CommandParserResults results = parser.parse(options, args); CommandParserResults results = parser.parse(options, args);
assertThat(results.results()).hasSize(0); assertThat(results.results()).hasSize(0);
assertThat(results.errors()).hasSize(2); assertThat(results.errors()).hasSize(2);
assertThat(results.errors()).satisfiesExactly(
e -> {
assertThat(e).isInstanceOf(UnrecognisedOptionException.class);
},
e -> {
assertThat(e).isInstanceOf(MissingOptionException.class);
}
);
assertThat(results.positional()).hasSize(1); assertThat(results.positional()).hasSize(1);
} }
@Test
public void testDashOptionValueDoNotError() {
// gh-651
CommandOption option1 = longOption("arg1");
List<CommandOption> options = Arrays.asList(option1);
String[] args = new String[]{"--arg1", "-1"};
CommandParserResults results = parser.parse(options, args);
assertThat(results.results()).hasSize(1);
assertThat(results.errors()).hasSize(0);
assertThat(results.positional()).hasSize(0);
}
@Test @Test
public void testPositionDoesNotAffectRequiredErrorWithOtherErrors() { public void testPositionDoesNotAffectRequiredErrorWithOtherErrors() {
// gh-601 // gh-601