Support cumulative positional arguments.

This commit is contained in:
Alec Thomas
2018-05-17 20:16:12 +10:00
parent b9d002b746
commit 31fe51f9d8
5 changed files with 49 additions and 8 deletions
+1
View File
@@ -100,6 +100,7 @@ func buildNode(v reflect.Value) *Node {
if arg {
node.Positional = append(node.Positional, &value)
} else {
value.Flag = true
node.Flags = append(node.Flags, &Flag{
Value: value,
Short: short,
+18 -7
View File
@@ -181,18 +181,20 @@ func timeDecoder(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 {
return err
return fmt.Errorf("invalid int %q", value)
}
target.SetInt(n)
return nil
}
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 {
return err
return fmt.Errorf("invalid uint %q", value)
}
target.SetUint(n)
return nil
@@ -200,9 +202,10 @@ func uintDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) error
func floatDecoder(bits int) DecoderFunc {
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 {
return err
return fmt.Errorf("invalid float %q", value)
}
target.SetFloat(n)
return nil
@@ -215,7 +218,15 @@ func sliceDecoder(ctx *DecoderContext, scan *Scanner, target reflect.Value) erro
if !ok {
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)
if childDecoder == nil {
return fmt.Errorf("no decoder for element type of %s", target.Type())
+13 -1
View File
@@ -76,7 +76,7 @@ func TestResetWithDefaults(t *testing.T) {
require.Equal(t, "default", cli.FlagWithDefault)
}
func TestSlice(t *testing.T) {
func TestFlagSlice(t *testing.T) {
var cli struct {
Slice []int `help:""`
}
@@ -86,6 +86,18 @@ func TestSlice(t *testing.T) {
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) {
var cli struct {
Keys map[string]string `help:""`
+1
View File
@@ -21,6 +21,7 @@ type Node struct {
}
type Value struct {
Flag bool // True if flag, false if positional argument.
Name string
Help string
Decoder Decoder
+16
View File
@@ -86,6 +86,22 @@ func (s *Scanner) PopValue(context string) string {
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 {
if len(s.args) == 0 {
return Token{Type: EOLToken}