Support cumulative positional arguments.
This commit is contained in:
@@ -100,6 +100,7 @@ func buildNode(v reflect.Value) *Node {
|
|||||||
if arg {
|
if arg {
|
||||||
node.Positional = append(node.Positional, &value)
|
node.Positional = append(node.Positional, &value)
|
||||||
} else {
|
} else {
|
||||||
|
value.Flag = true
|
||||||
node.Flags = append(node.Flags, &Flag{
|
node.Flags = append(node.Flags, &Flag{
|
||||||
Value: value,
|
Value: value,
|
||||||
Short: short,
|
Short: short,
|
||||||
|
|||||||
+18
-7
@@ -181,18 +181,20 @@ func timeDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func intDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) error {
|
func intDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) error {
|
||||||
n, err := strconv.ParseInt(scan.PopValue("int"), 10, 64)
|
value := scan.PopValue("int")
|
||||||
|
n, err := strconv.ParseInt(value, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("invalid int %q", value)
|
||||||
}
|
}
|
||||||
target.SetInt(n)
|
target.SetInt(n)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func uintDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) error {
|
func uintDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) error {
|
||||||
n, err := strconv.ParseUint(scan.PopValue("uint"), 10, 64)
|
value := scan.PopValue("uint")
|
||||||
|
n, err := strconv.ParseUint(value, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("invalid uint %q", value)
|
||||||
}
|
}
|
||||||
target.SetUint(n)
|
target.SetUint(n)
|
||||||
return nil
|
return nil
|
||||||
@@ -200,9 +202,10 @@ func uintDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) error
|
|||||||
|
|
||||||
func floatDecoder(bits int) DecoderFunc {
|
func floatDecoder(bits int) DecoderFunc {
|
||||||
return func(ctx *DecoderContext, scan *Scanner, target reflect.Value) error {
|
return func(ctx *DecoderContext, scan *Scanner, target reflect.Value) error {
|
||||||
n, err := strconv.ParseFloat(scan.PopValue("float"), bits)
|
value := scan.PopValue("float")
|
||||||
|
n, err := strconv.ParseFloat(value, bits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("invalid float %q", value)
|
||||||
}
|
}
|
||||||
target.SetFloat(n)
|
target.SetFloat(n)
|
||||||
return nil
|
return nil
|
||||||
@@ -215,7 +218,15 @@ func sliceDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) erro
|
|||||||
if !ok {
|
if !ok {
|
||||||
sep = ","
|
sep = ","
|
||||||
}
|
}
|
||||||
childScanner := Scan(strings.Split(scan.PopValue("list"), sep)...)
|
var childScanner *Scanner
|
||||||
|
if ctx.Value.Flag {
|
||||||
|
childScanner = Scan(strings.Split(scan.PopValue("list"), sep)...)
|
||||||
|
} else {
|
||||||
|
tokens := scan.PopUntil(func(t Token) bool {
|
||||||
|
return !t.IsValue() || (t.Type == UntypedToken && strings.HasPrefix(t.Value, "-"))
|
||||||
|
})
|
||||||
|
childScanner = Scan(tokens...)
|
||||||
|
}
|
||||||
childDecoder := DecoderForType(el)
|
childDecoder := DecoderForType(el)
|
||||||
if childDecoder == nil {
|
if childDecoder == nil {
|
||||||
return fmt.Errorf("no decoder for element type of %s", target.Type())
|
return fmt.Errorf("no decoder for element type of %s", target.Type())
|
||||||
|
|||||||
+13
-1
@@ -76,7 +76,7 @@ func TestResetWithDefaults(t *testing.T) {
|
|||||||
require.Equal(t, "default", cli.FlagWithDefault)
|
require.Equal(t, "default", cli.FlagWithDefault)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSlice(t *testing.T) {
|
func TestFlagSlice(t *testing.T) {
|
||||||
var cli struct {
|
var cli struct {
|
||||||
Slice []int `help:""`
|
Slice []int `help:""`
|
||||||
}
|
}
|
||||||
@@ -86,6 +86,18 @@ func TestSlice(t *testing.T) {
|
|||||||
require.Equal(t, []int{1, 2, 3}, cli.Slice)
|
require.Equal(t, []int{1, 2, 3}, cli.Slice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestArgSlice(t *testing.T) {
|
||||||
|
var cli struct {
|
||||||
|
Slice []int `help:"" arg:""`
|
||||||
|
Flag bool `help:""`
|
||||||
|
}
|
||||||
|
parser := mustNew(t, &cli)
|
||||||
|
_, err := parser.Parse([]string{"1", "2", "3", "--flag"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []int{1, 2, 3}, cli.Slice)
|
||||||
|
require.Equal(t, true, cli.Flag)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnsupportedfieldErrors(t *testing.T) {
|
func TestUnsupportedfieldErrors(t *testing.T) {
|
||||||
var cli struct {
|
var cli struct {
|
||||||
Keys map[string]string `help:""`
|
Keys map[string]string `help:""`
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ type Node struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Value struct {
|
type Value struct {
|
||||||
|
Flag bool // True if flag, false if positional argument.
|
||||||
Name string
|
Name string
|
||||||
Help string
|
Help string
|
||||||
Decoder Decoder
|
Decoder Decoder
|
||||||
|
|||||||
+16
@@ -86,6 +86,22 @@ func (s *Scanner) PopValue(context string) string {
|
|||||||
return t.Value
|
return t.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PopWhile predicate returns true.
|
||||||
|
func (s *Scanner) PopWhile(predicate func(Token) bool) (values []string) {
|
||||||
|
for predicate(s.Peek()) {
|
||||||
|
values = append(values, s.Pop().Value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopUntil predicate returns true.
|
||||||
|
func (s *Scanner) PopUntil(predicate func(Token) bool) (values []string) {
|
||||||
|
for !predicate(s.Peek()) {
|
||||||
|
values = append(values, s.Pop().Value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Scanner) Peek() Token {
|
func (s *Scanner) Peek() Token {
|
||||||
if len(s.args) == 0 {
|
if len(s.args) == 0 {
|
||||||
return Token{Type: EOLToken}
|
return Token{Type: EOLToken}
|
||||||
|
|||||||
Reference in New Issue
Block a user