aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/alexflint/go-arg/reflect.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/alexflint/go-arg/reflect.go')
-rw-r--r--vendor/github.com/alexflint/go-arg/reflect.go107
1 files changed, 107 insertions, 0 deletions
diff --git a/vendor/github.com/alexflint/go-arg/reflect.go b/vendor/github.com/alexflint/go-arg/reflect.go
new file mode 100644
index 0000000..cd80be7
--- /dev/null
+++ b/vendor/github.com/alexflint/go-arg/reflect.go
@@ -0,0 +1,107 @@
1package arg
2
3import (
4 "encoding"
5 "fmt"
6 "reflect"
7 "unicode"
8 "unicode/utf8"
9
10 scalar "github.com/alexflint/go-scalar"
11)
12
13var textUnmarshalerType = reflect.TypeOf([]encoding.TextUnmarshaler{}).Elem()
14
15// cardinality tracks how many tokens are expected for a given spec
16// - zero is a boolean, which does to expect any value
17// - one is an ordinary option that will be parsed from a single token
18// - multiple is a slice or map that can accept zero or more tokens
19type cardinality int
20
21const (
22 zero cardinality = iota
23 one
24 multiple
25 unsupported
26)
27
28func (k cardinality) String() string {
29 switch k {
30 case zero:
31 return "zero"
32 case one:
33 return "one"
34 case multiple:
35 return "multiple"
36 case unsupported:
37 return "unsupported"
38 default:
39 return fmt.Sprintf("unknown(%d)", int(k))
40 }
41}
42
43// cardinalityOf returns true if the type can be parsed from a string
44func cardinalityOf(t reflect.Type) (cardinality, error) {
45 if scalar.CanParse(t) {
46 if isBoolean(t) {
47 return zero, nil
48 }
49 return one, nil
50 }
51
52 // look inside pointer types
53 if t.Kind() == reflect.Ptr {
54 t = t.Elem()
55 }
56
57 // look inside slice and map types
58 switch t.Kind() {
59 case reflect.Slice:
60 if !scalar.CanParse(t.Elem()) {
61 return unsupported, fmt.Errorf("cannot parse into %v because %v not supported", t, t.Elem())
62 }
63 return multiple, nil
64 case reflect.Map:
65 if !scalar.CanParse(t.Key()) {
66 return unsupported, fmt.Errorf("cannot parse into %v because key type %v not supported", t, t.Elem())
67 }
68 if !scalar.CanParse(t.Elem()) {
69 return unsupported, fmt.Errorf("cannot parse into %v because value type %v not supported", t, t.Elem())
70 }
71 return multiple, nil
72 default:
73 return unsupported, fmt.Errorf("cannot parse into %v", t)
74 }
75}
76
77// isBoolean returns true if the type can be parsed from a single string
78func isBoolean(t reflect.Type) bool {
79 switch {
80 case t.Implements(textUnmarshalerType):
81 return false
82 case t.Kind() == reflect.Bool:
83 return true
84 case t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Bool:
85 return true
86 default:
87 return false
88 }
89}
90
91// isExported returns true if the struct field name is exported
92func isExported(field string) bool {
93 r, _ := utf8.DecodeRuneInString(field) // returns RuneError for empty string or invalid UTF8
94 return unicode.IsLetter(r) && unicode.IsUpper(r)
95}
96
97// isZero returns true if v contains the zero value for its type
98func isZero(v reflect.Value) bool {
99 t := v.Type()
100 if t.Kind() == reflect.Slice || t.Kind() == reflect.Map {
101 return v.IsNil()
102 }
103 if !t.Comparable() {
104 return false
105 }
106 return v.Interface() == reflect.Zero(t).Interface()
107}