Merge pull request #64 from alexflint/repeated-unmarshaltext
Fix repeated arguments implementing TextUnmarshaler
This commit is contained in:
commit
074ee5f759
39
parse.go
39
parse.go
|
@ -159,7 +159,7 @@ func NewParser(config Config, dests ...interface{}) (*Parser, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether this field is supported. It's good to do this here rather than
|
// Check whether this field is supported. It's good to do this here rather than
|
||||||
// wait until setScalar because it means that a program with invalid argument
|
// wait until ParseValue because it means that a program with invalid argument
|
||||||
// fields will always fail regardless of whether the arguments it received
|
// fields will always fail regardless of whether the arguments it received
|
||||||
// exercised those fields.
|
// exercised those fields.
|
||||||
var parseable bool
|
var parseable bool
|
||||||
|
@ -275,7 +275,7 @@ func process(specs []*spec, args []string) error {
|
||||||
}
|
}
|
||||||
if spec.env != "" {
|
if spec.env != "" {
|
||||||
if value, found := os.LookupEnv(spec.env); found {
|
if value, found := os.LookupEnv(spec.env); found {
|
||||||
err := setScalar(spec.dest, value)
|
err := scalar.ParseValue(spec.dest, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error processing environment variable %s: %v", spec.env, err)
|
return fmt.Errorf("error processing environment variable %s: %v", spec.env, err)
|
||||||
}
|
}
|
||||||
|
@ -355,7 +355,7 @@ func process(specs []*spec, args []string) error {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
err := setScalar(spec.dest, value)
|
err := scalar.ParseValue(spec.dest, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error processing %s: %v", arg, err)
|
return fmt.Errorf("error processing %s: %v", arg, err)
|
||||||
}
|
}
|
||||||
|
@ -374,7 +374,7 @@ func process(specs []*spec, args []string) error {
|
||||||
}
|
}
|
||||||
positionals = nil
|
positionals = nil
|
||||||
} else if len(positionals) > 0 {
|
} else if len(positionals) > 0 {
|
||||||
err := setScalar(spec.dest, positionals[0])
|
err := scalar.ParseValue(spec.dest, positionals[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error processing %s: %v", spec.long, err)
|
return fmt.Errorf("error processing %s: %v", spec.long, err)
|
||||||
}
|
}
|
||||||
|
@ -426,7 +426,7 @@ func setSlice(dest reflect.Value, values []string, trunc bool) error {
|
||||||
|
|
||||||
var ptr bool
|
var ptr bool
|
||||||
elem := dest.Type().Elem()
|
elem := dest.Type().Elem()
|
||||||
if elem.Kind() == reflect.Ptr {
|
if elem.Kind() == reflect.Ptr && !elem.Implements(textUnmarshalerType) {
|
||||||
ptr = true
|
ptr = true
|
||||||
elem = elem.Elem()
|
elem = elem.Elem()
|
||||||
}
|
}
|
||||||
|
@ -438,7 +438,7 @@ func setSlice(dest reflect.Value, values []string, trunc bool) error {
|
||||||
|
|
||||||
for _, s := range values {
|
for _, s := range values {
|
||||||
v := reflect.New(elem)
|
v := reflect.New(elem)
|
||||||
if err := setScalar(v.Elem(), s); err != nil {
|
if err := scalar.ParseValue(v.Elem(), s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !ptr {
|
if !ptr {
|
||||||
|
@ -451,7 +451,8 @@ func setSlice(dest reflect.Value, values []string, trunc bool) error {
|
||||||
|
|
||||||
// canParse returns true if the type can be parsed from a string
|
// canParse returns true if the type can be parsed from a string
|
||||||
func canParse(t reflect.Type) (parseable, boolean, multiple bool) {
|
func canParse(t reflect.Type) (parseable, boolean, multiple bool) {
|
||||||
parseable, boolean = isScalar(t)
|
parseable = scalar.CanParse(t)
|
||||||
|
boolean = isBoolean(t)
|
||||||
if parseable {
|
if parseable {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -466,7 +467,8 @@ func canParse(t reflect.Type) (parseable, boolean, multiple bool) {
|
||||||
t = t.Elem()
|
t = t.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
parseable, boolean = isScalar(t)
|
parseable = scalar.CanParse(t)
|
||||||
|
boolean = isBoolean(t)
|
||||||
if parseable {
|
if parseable {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -476,7 +478,8 @@ func canParse(t reflect.Type) (parseable, boolean, multiple bool) {
|
||||||
t = t.Elem()
|
t = t.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
parseable, boolean = isScalar(t)
|
parseable = scalar.CanParse(t)
|
||||||
|
boolean = isBoolean(t)
|
||||||
if parseable {
|
if parseable {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -486,22 +489,16 @@ func canParse(t reflect.Type) (parseable, boolean, multiple bool) {
|
||||||
|
|
||||||
var textUnmarshalerType = reflect.TypeOf([]encoding.TextUnmarshaler{}).Elem()
|
var textUnmarshalerType = reflect.TypeOf([]encoding.TextUnmarshaler{}).Elem()
|
||||||
|
|
||||||
// isScalar returns true if the type can be parsed from a single string
|
// isBoolean returns true if the type can be parsed from a single string
|
||||||
func isScalar(t reflect.Type) (parseable, boolean bool) {
|
func isBoolean(t reflect.Type) bool {
|
||||||
parseable = scalar.CanParse(t)
|
|
||||||
switch {
|
switch {
|
||||||
case t.Implements(textUnmarshalerType):
|
case t.Implements(textUnmarshalerType):
|
||||||
return parseable, false
|
return false
|
||||||
case t.Kind() == reflect.Bool:
|
case t.Kind() == reflect.Bool:
|
||||||
return parseable, true
|
return true
|
||||||
case t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Bool:
|
case t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Bool:
|
||||||
return parseable, true
|
return true
|
||||||
default:
|
default:
|
||||||
return parseable, false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set a value from a string
|
|
||||||
func setScalar(v reflect.Value, s string) error {
|
|
||||||
return scalar.ParseValue(v, s)
|
|
||||||
}
|
|
||||||
|
|
|
@ -599,6 +599,32 @@ func TestTextUnmarshaler(t *testing.T) {
|
||||||
assert.Equal(t, 3, args.Foo.val)
|
assert.Equal(t, 3, args.Foo.val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRepeatedTextUnmarshaler(t *testing.T) {
|
||||||
|
// fields that implement TextUnmarshaler should be parsed using that interface
|
||||||
|
var args struct {
|
||||||
|
Foo []*textUnmarshaler
|
||||||
|
}
|
||||||
|
err := parse("--foo abc d ef", &args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, args.Foo, 3)
|
||||||
|
assert.Equal(t, 3, args.Foo[0].val)
|
||||||
|
assert.Equal(t, 1, args.Foo[1].val)
|
||||||
|
assert.Equal(t, 2, args.Foo[2].val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPositionalTextUnmarshaler(t *testing.T) {
|
||||||
|
// fields that implement TextUnmarshaler should be parsed using that interface
|
||||||
|
var args struct {
|
||||||
|
Foo []*textUnmarshaler `arg:"positional"`
|
||||||
|
}
|
||||||
|
err := parse("abc d ef", &args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, args.Foo, 3)
|
||||||
|
assert.Equal(t, 3, args.Foo[0].val)
|
||||||
|
assert.Equal(t, 1, args.Foo[1].val)
|
||||||
|
assert.Equal(t, 2, args.Foo[2].val)
|
||||||
|
}
|
||||||
|
|
||||||
type boolUnmarshaler bool
|
type boolUnmarshaler bool
|
||||||
|
|
||||||
func (p *boolUnmarshaler) UnmarshalText(b []byte) error {
|
func (p *boolUnmarshaler) UnmarshalText(b []byte) error {
|
||||||
|
|
Loading…
Reference in New Issue