-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathslices.go
153 lines (142 loc) · 3.71 KB
/
slices.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package flagx
import (
"fmt"
"reflect"
"strconv"
"strings"
)
type IntSlice struct {
Slice *[]int
}
func (is IntSlice) String() string {
if is.Slice == nil {
// When called by flag.isZeroValue
return ""
}
if *is.Slice == nil {
return ""
}
str := make([]string, len(*is.Slice))
for i := range str {
str[i] = strconv.Itoa((*is.Slice)[i])
}
return strings.Join(str, ",")
}
func (is IntSlice) Set(s string) (err error) {
str := strings.Split(s, ",")
for _, s := range str {
s = strings.TrimSpace(s)
n, err := strconv.ParseInt(s, 0, 0)
if err != nil {
return fmt.Errorf("%q: %s", s, err)
}
*is.Slice = append(*is.Slice, int(n))
}
return nil
}
func (is IntSlice) Get() interface{} {
return *is.Slice
}
// Slice wraps any pointer to a slice to expose it as a [flag.Value].
//
// The element type of the slice may implement [flag.Value]. In that case the
// Set() method will be called on the target element.
// The element type of the slice may implement [encoding.TextUnmarshaler]. In
// that case the UnmarshalText() method will be called on the target element.
//
// The parse func is optional. If it is set, it must return a value
// assignable to an element of the slice. If the returned value is a bare
// string, it will pass through Set() or UnmarshalText() if the type implements
// it (see above).
func Slice(sl interface{}, separator string, parse func(string) (interface{}, error)) Value {
v := reflect.ValueOf(sl)
if v.Type().Kind() != reflect.Ptr {
panic("pointer expected")
}
if v.IsNil() {
panic("non-nil pointer expected")
}
v = v.Elem() // v now wraps the slice
if v.Kind() != reflect.Slice {
panic("non-nil pointer to a slice expected")
}
itemType := v.Type().Elem()
setter := setterFor(itemType)
if parse == nil {
if setter == nil {
panic(fmt.Sprintf("invalid slice type: %s doesn't implement encoding.TextUnmarshaler or flag.Value", v.Type()))
}
if itemType.Kind() == reflect.Interface {
panic("a parse function must be provided to build a concrete value")
}
} else {
if setter == nil {
setter = func(target reflect.Value, value string) error {
v, err := parse(value)
if err != nil {
return err
}
// FIXME This need more testing!
if itemType.Kind() == reflect.Interface {
if itemType.NumMethod() == 0 {
target.Set(reflect.ValueOf(&v).Elem())
} else {
target.Set(reflect.ValueOf(&v).Elem().Elem().Convert(itemType))
}
return nil
}
target.Set(reflect.ValueOf(&v).Elem().Elem())
return nil
}
} else {
setString := setter
setter = func(target reflect.Value, value string) error {
v, err := parse(value)
if err != nil {
return err
}
if s, isString := v.(string); isString {
// Go through Set() or UnmarshalText()
return setString(target, s)
}
//fmt.Printf("%T -> %s\n", v, target.Type())
//fmt.Printf("%s -> %s\n", reflect.ValueOf(&v).Elem().Elem().Type(), target.Type())
target.Set(reflect.ValueOf(&v).Elem().Elem())
return nil
}
}
}
return &slice{v, itemType, separator, setter}
}
type slice struct {
slice reflect.Value
itemType reflect.Type
separator string
setString func(reflect.Value, string) error
}
func (sl *slice) String() string {
return ""
}
func (sl *slice) appnd(s string) error {
v := reflect.New(sl.itemType)
err := sl.setString(v.Elem(), s)
if err != nil {
return err
}
sl.slice.Set(reflect.Append(sl.slice, v.Elem()))
return nil
}
func (sl *slice) Set(s string) error {
if len(sl.separator) != 0 {
for _, item := range strings.Split(s, sl.separator) {
if err := sl.appnd(item); err != nil {
return err
}
}
return nil
}
return sl.appnd(s)
}
func (sl *slice) Get() interface{} {
return sl.slice.Interface()
}