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:
Janne Valkealahti
2023-01-13 15:11:38 +00:00
parent b10786814a
commit 95e2829dc3
8 changed files with 266 additions and 20 deletions

View File

@@ -4,7 +4,7 @@ ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Sometimes, you want to have more fine control of how many parameters with an option
are processed when parsing operations happen. Arity is defined as min and max
values, where min must be a positive integer and max has to be more or equal to min.
values, where min must be zero or a positive integer and max has to be more or equal to min.
====
[source, java, indent=0]
@@ -14,7 +14,7 @@ include::{snippets}/OptionSnippets.java[tag=option-registration-arityints]
====
Arity can also be defined as an `OptionArity` enum, which are shortcuts
within the following table:
shown in below table <<using-shell-options-arity-optionarity-table>>.
====
[source, java, indent=0]
@@ -23,6 +23,7 @@ include::{snippets}/OptionSnippets.java[tag=option-registration-arityenum]
----
====
[[using-shell-options-arity-optionarity-table]]
.OptionArity
|===
|Value |min/max
@@ -51,3 +52,35 @@ The annotation model supports defining only the max value of an arity.
include::{snippets}/OptionSnippets.java[tag=option-with-annotation-arity]
----
====
One of a use cases to manually define arity is to impose restrictions how
many parameters option accepts.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-aritystrings-sample]
----
====
In above example we have option _arg1_ and it's defined as type _String[]_. Arity
defines that it needs at least 1 parameter and not more that 2. As seen in below
spesific exceptions _TooManyArgumentsOptionException_ and
_NotEnoughArgumentsOptionException_ are thrown to indicate arity mismatch.
====
[source, bash]
----
shell:>e2e reg arity-errors --arg1
Not enough arguments --arg1 requires at least 1.
shell:>e2e reg arity-errors --arg1 one
Hello [one]
shell:>e2e reg arity-errors --arg1 one two
Hello [one, two]
shell:>e2e reg arity-errors --arg1 one two three
Too many arguments --arg1 requires at most 2.
----
====

View File

@@ -10,3 +10,61 @@ Positional information is mostly related to a command target method:
include::{snippets}/OptionSnippets.java[tag=option-registration-positional]
----
====
NOTE: Be careful with positional parameters as it may soon
become confusing which options those are mapped to.
Usually arguments are mapped to an option when those are defined in a
command line whether it's a long or short option. Generally speaking
there are _options_, _option arguments_ and _arguments_ where latter
are the ones which are not mapped to any spesific option.
Unrecognised arguments can then have a secondary mapping logic where
positional information is important. With option position you're
essentially telling command parsing how to interpret plain raw
ambiguous arguments.
Let's look what happens when we don't define a position.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-aritystrings-noposition]
----
====
Option _arg1_ is required and there is no info what to do with argument
`one` resulting error for missing option.
====
[source, bash]
----
shell:>arity-strings-1 one
Missing mandatory option --arg1.
----
====
Now let's define a position `0`.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-aritystrings-position]
----
====
Arguments are processed until we get up to 2 arguments.
====
[source, bash]
----
shell:>arity-strings-2 one
Hello [one]
shell:>arity-strings-2 one two
Hello [one, two]
shell:>arity-strings-2 one two three
Hello [one, two]
----
====

View File

@@ -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.
@@ -15,6 +15,8 @@
*/
package org.springframework.shell.docs;
import java.util.Arrays;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.CommandRegistration.OptionArity;
import org.springframework.shell.standard.ShellOption;
@@ -144,6 +146,61 @@ public class OptionSnippets {
.build();
// end::option-registration-arityints[]
// tag::option-registration-aritystrings-sample[]
CommandRegistration.builder()
.command("arity-errors")
.withOption()
.longNames("arg1")
.type(String[].class)
.required()
.arity(1, 2)
.and()
.withTarget()
.function(ctx -> {
String[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + Arrays.asList(arg1);
})
.and()
.build();
// end::option-registration-aritystrings-sample[]
// tag::option-registration-aritystrings-position[]
CommandRegistration.builder()
.command("arity-strings-2")
.withOption()
.longNames("arg1")
.required()
.type(String[].class)
.arity(0, 2)
.position(0)
.and()
.withTarget()
.function(ctx -> {
String[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + Arrays.asList(arg1);
})
.and()
.build();
// end::option-registration-aritystrings-position[]
// tag::option-registration-aritystrings-noposition[]
CommandRegistration.builder()
.command("arity-strings-1")
.withOption()
.longNames("arg1")
.required()
.type(String[].class)
.arity(0, 2)
.and()
.withTarget()
.function(ctx -> {
String[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + Arrays.asList(arg1);
})
.and()
.build();
// end::option-registration-aritystrings-noposition[]
// tag::option-registration-optional[]
CommandRegistration.builder()
.withOption()