diff --git a/parse.go b/parse.go index 2a3442a..0633bb6 100644 --- a/parse.go +++ b/parse.go @@ -1,7 +1,6 @@ package arg import ( - "encoding" "encoding/csv" "errors" "fmt" @@ -659,60 +658,6 @@ func setSlice(dest reflect.Value, values []string, trunc bool) error { return nil } -// canParse returns true if the type can be parsed from a string -func canParse(t reflect.Type) (parseable, boolean, multiple bool) { - parseable = scalar.CanParse(t) - boolean = isBoolean(t) - if parseable { - return - } - - // Look inside pointer types - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - // Look inside slice types - if t.Kind() == reflect.Slice { - multiple = true - t = t.Elem() - } - - parseable = scalar.CanParse(t) - boolean = isBoolean(t) - if parseable { - return - } - - // Look inside pointer types (again, in case of []*Type) - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - - parseable = scalar.CanParse(t) - boolean = isBoolean(t) - if parseable { - return - } - - return false, false, false -} - -var textUnmarshalerType = reflect.TypeOf([]encoding.TextUnmarshaler{}).Elem() - -// isBoolean returns true if the type can be parsed from a single string -func isBoolean(t reflect.Type) bool { - switch { - case t.Implements(textUnmarshalerType): - return false - case t.Kind() == reflect.Bool: - return true - case t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Bool: - return true - default: - return false - } -} - // findOption finds an option from its name, or returns null if no spec is found func findOption(specs []*spec, name string) *spec { for _, spec := range specs { diff --git a/reflect.go b/reflect.go new file mode 100644 index 0000000..32efe04 --- /dev/null +++ b/reflect.go @@ -0,0 +1,64 @@ +package arg + +import ( + "encoding" + "reflect" + + scalar "github.com/alexflint/go-scalar" +) + +var textUnmarshalerType = reflect.TypeOf([]encoding.TextUnmarshaler{}).Elem() + +// This file contains miscellaneous reflection utilities + +// canParse returns true if the type can be parsed from a string +func canParse(t reflect.Type) (parseable, boolean, multiple bool) { + parseable = scalar.CanParse(t) + boolean = isBoolean(t) + if parseable { + return + } + + // Look inside pointer types + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + // Look inside slice types + if t.Kind() == reflect.Slice { + multiple = true + t = t.Elem() + } + + parseable = scalar.CanParse(t) + boolean = isBoolean(t) + if parseable { + return + } + + // Look inside pointer types (again, in case of []*Type) + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + + parseable = scalar.CanParse(t) + boolean = isBoolean(t) + if parseable { + return + } + + return false, false, false +} + +// isBoolean returns true if the type can be parsed from a single string +func isBoolean(t reflect.Type) bool { + switch { + case t.Implements(textUnmarshalerType): + return false + case t.Kind() == reflect.Bool: + return true + case t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Bool: + return true + default: + return false + } +} diff --git a/reflect_test.go b/reflect_test.go new file mode 100644 index 0000000..0f285f7 --- /dev/null +++ b/reflect_test.go @@ -0,0 +1,40 @@ +package arg + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +func assertCanParse(t *testing.T, typ reflect.Type, parseable, boolean, multiple bool) { + p, b, m := canParse(typ) + assert.Equal(t, parseable, p, "expected %v to have parseable=%v but was %v", typ, parseable, p) + assert.Equal(t, boolean, b, "expected %v to have boolean=%v but was %v", typ, boolean, b) + assert.Equal(t, multiple, m, "expected %v to have multiple=%v but was %v", typ, multiple, m) +} + +func TestCanParse(t *testing.T) { + var b bool + var i int + var s string + var f float64 + var bs []bool + var is []int + + assertCanParse(t, reflect.TypeOf(b), true, true, false) + assertCanParse(t, reflect.TypeOf(i), true, false, false) + assertCanParse(t, reflect.TypeOf(s), true, false, false) + assertCanParse(t, reflect.TypeOf(f), true, false, false) + + assertCanParse(t, reflect.TypeOf(&b), true, true, false) + assertCanParse(t, reflect.TypeOf(&s), true, false, false) + assertCanParse(t, reflect.TypeOf(&i), true, false, false) + assertCanParse(t, reflect.TypeOf(&f), true, false, false) + + assertCanParse(t, reflect.TypeOf(bs), true, true, true) + assertCanParse(t, reflect.TypeOf(&bs), true, true, true) + + assertCanParse(t, reflect.TypeOf(is), true, false, true) + assertCanParse(t, reflect.TypeOf(&is), true, false, true) +}