1 package cli
2
3 import (
4 "flag"
5 "reflect"
6 )
7
8 type (
9
10
11
12 SliceFlag[T SliceFlagTarget[E], S ~[]E, E any] struct {
13 Target T
14 Value S
15 Destination *S
16 }
17
18
19
20
21
22 SliceFlagTarget[E any] interface {
23 Flag
24 RequiredFlag
25 DocGenerationFlag
26 VisibleFlag
27 CategorizableFlag
28
29
30
31 SetValue(slice []E)
32
33
34 SetDestination(slice []E)
35
36 GetDestination() []E
37 }
38
39
40
41 MultiStringFlag = SliceFlag[*StringSliceFlag, []string, string]
42
43
44
45 MultiFloat64Flag = SliceFlag[*Float64SliceFlag, []float64, float64]
46
47
48
49 MultiInt64Flag = SliceFlag[*Int64SliceFlag, []int64, int64]
50
51
52
53 MultiIntFlag = SliceFlag[*IntSliceFlag, []int, int]
54
55 flagValueHook struct {
56 value Generic
57 hook func()
58 }
59 )
60
61 var (
62
63
64 _ SliceFlagTarget[string] = (*StringSliceFlag)(nil)
65 _ SliceFlagTarget[string] = (*SliceFlag[*StringSliceFlag, []string, string])(nil)
66 _ SliceFlagTarget[string] = (*MultiStringFlag)(nil)
67 _ SliceFlagTarget[float64] = (*MultiFloat64Flag)(nil)
68 _ SliceFlagTarget[int64] = (*MultiInt64Flag)(nil)
69 _ SliceFlagTarget[int] = (*MultiIntFlag)(nil)
70
71 _ Generic = (*flagValueHook)(nil)
72 _ Serializer = (*flagValueHook)(nil)
73 )
74
75 func (x *SliceFlag[T, S, E]) Apply(set *flag.FlagSet) error {
76 x.Target.SetValue(x.convertSlice(x.Value))
77
78 destination := x.Destination
79 if destination == nil {
80 x.Target.SetDestination(nil)
81
82 return x.Target.Apply(set)
83 }
84
85 x.Target.SetDestination(x.convertSlice(*destination))
86
87 return applyFlagValueHook(set, x.Target.Apply, func() {
88 *destination = x.Target.GetDestination()
89 })
90 }
91
92 func (x *SliceFlag[T, S, E]) convertSlice(slice S) []E {
93 result := make([]E, len(slice))
94 copy(result, slice)
95 return result
96 }
97
98 func (x *SliceFlag[T, S, E]) SetValue(slice S) {
99 x.Value = slice
100 }
101
102 func (x *SliceFlag[T, S, E]) SetDestination(slice S) {
103 if slice != nil {
104 x.Destination = &slice
105 } else {
106 x.Destination = nil
107 }
108 }
109
110 func (x *SliceFlag[T, S, E]) GetDestination() S {
111 if destination := x.Destination; destination != nil {
112 return *destination
113 }
114 return nil
115 }
116
117 func (x *SliceFlag[T, S, E]) String() string { return x.Target.String() }
118 func (x *SliceFlag[T, S, E]) Names() []string { return x.Target.Names() }
119 func (x *SliceFlag[T, S, E]) IsSet() bool { return x.Target.IsSet() }
120 func (x *SliceFlag[T, S, E]) IsRequired() bool { return x.Target.IsRequired() }
121 func (x *SliceFlag[T, S, E]) TakesValue() bool { return x.Target.TakesValue() }
122 func (x *SliceFlag[T, S, E]) GetUsage() string { return x.Target.GetUsage() }
123 func (x *SliceFlag[T, S, E]) GetValue() string { return x.Target.GetValue() }
124 func (x *SliceFlag[T, S, E]) GetDefaultText() string { return x.Target.GetDefaultText() }
125 func (x *SliceFlag[T, S, E]) GetEnvVars() []string { return x.Target.GetEnvVars() }
126 func (x *SliceFlag[T, S, E]) IsVisible() bool { return x.Target.IsVisible() }
127 func (x *SliceFlag[T, S, E]) GetCategory() string { return x.Target.GetCategory() }
128
129 func (x *flagValueHook) Set(value string) error {
130 if err := x.value.Set(value); err != nil {
131 return err
132 }
133 x.hook()
134 return nil
135 }
136
137 func (x *flagValueHook) String() string {
138
139 isZeroValue := func(f flag.Value, v string) bool {
140
168
169
170
171 typ := reflect.TypeOf(f)
172 var z reflect.Value
173 if typ.Kind() == reflect.Pointer {
174 z = reflect.New(typ.Elem())
175 } else {
176 z = reflect.Zero(typ)
177 }
178 return v == z.Interface().(flag.Value).String()
179 }
180 if x.value != nil {
181
182 if s := x.value.String(); !isZeroValue(x.value, s) {
183 return s
184 }
185 }
186 return ``
187 }
188
189 func (x *flagValueHook) Serialize() string {
190 if value, ok := x.value.(Serializer); ok {
191 return value.Serialize()
192 }
193 return x.String()
194 }
195
196
197 func applyFlagValueHook(set *flag.FlagSet, apply func(set *flag.FlagSet) error, hook func()) error {
198 if apply == nil || set == nil || hook == nil {
199 panic(`invalid input`)
200 }
201 var tmp flag.FlagSet
202 if err := apply(&tmp); err != nil {
203 return err
204 }
205 tmp.VisitAll(func(f *flag.Flag) { set.Var(&flagValueHook{value: f.Value, hook: hook}, f.Name, f.Usage) })
206 hook()
207 return nil
208 }
209
210
211
212 func newSliceFlagValue[R any, S ~[]E, E any](factory func(defaults ...E) *R, defaults S) *R {
213 if defaults == nil {
214 return nil
215 }
216 return factory(defaults...)
217 }
218
219
220 func unwrapFlagValue(v flag.Value) flag.Value {
221 for {
222 h, ok := v.(*flagValueHook)
223 if !ok {
224 return v
225 }
226 v = h.value
227 }
228 }
229
230
231
232 func (f *Float64SliceFlag) SetValue(slice []float64) {
233 f.Value = newSliceFlagValue(NewFloat64Slice, slice)
234 }
235
236 func (f *Float64SliceFlag) SetDestination(slice []float64) {
237 f.Destination = newSliceFlagValue(NewFloat64Slice, slice)
238 }
239
240 func (f *Float64SliceFlag) GetDestination() []float64 {
241 if destination := f.Destination; destination != nil {
242 return destination.Value()
243 }
244 return nil
245 }
246
247 func (f *Int64SliceFlag) SetValue(slice []int64) {
248 f.Value = newSliceFlagValue(NewInt64Slice, slice)
249 }
250
251 func (f *Int64SliceFlag) SetDestination(slice []int64) {
252 f.Destination = newSliceFlagValue(NewInt64Slice, slice)
253 }
254
255 func (f *Int64SliceFlag) GetDestination() []int64 {
256 if destination := f.Destination; destination != nil {
257 return destination.Value()
258 }
259 return nil
260 }
261
262 func (f *IntSliceFlag) SetValue(slice []int) {
263 f.Value = newSliceFlagValue(NewIntSlice, slice)
264 }
265
266 func (f *IntSliceFlag) SetDestination(slice []int) {
267 f.Destination = newSliceFlagValue(NewIntSlice, slice)
268 }
269
270 func (f *IntSliceFlag) GetDestination() []int {
271 if destination := f.Destination; destination != nil {
272 return destination.Value()
273 }
274 return nil
275 }
276
277 func (f *StringSliceFlag) SetValue(slice []string) {
278 f.Value = newSliceFlagValue(NewStringSlice, slice)
279 }
280
281 func (f *StringSliceFlag) SetDestination(slice []string) {
282 f.Destination = newSliceFlagValue(NewStringSlice, slice)
283 }
284
285 func (f *StringSliceFlag) GetDestination() []string {
286 if destination := f.Destination; destination != nil {
287 return destination.Value()
288 }
289 return nil
290 }
291
View as plain text