1package clihelpers
2// Copyright 2012 Jesse van den Kieboom. All rights reserved.
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file.
5// The source is taken from: https://raw.githubusercontent.com/jessevdk/go-flags/master/convert.go
6
7import (
8	"fmt"
9	"reflect"
10	"strconv"
11	"strings"
12	"time"
13)
14
15// Marshaler is the interface implemented by types that can marshal themselves
16// to a string representation of the flag.
17type Marshaler interface {
18	// MarshalFlag marshals a flag value to its string representation.
19	MarshalFlag() (string, error)
20}
21
22// Unmarshaler is the interface implemented by types that can unmarshal a flag
23// argument to themselves. The provided value is directly passed from the
24// command line.
25type Unmarshaler interface {
26	// UnmarshalFlag unmarshals a string value representation to the flag
27	// value (which therefore needs to be a pointer receiver).
28	UnmarshalFlag(value string) error
29}
30
31func getBase(options reflect.StructTag, base int) (int, error) {
32	sbase := options.Get("base")
33
34	var err error
35	var ivbase int64
36
37	if sbase != "" {
38		ivbase, err = strconv.ParseInt(sbase, 10, 32)
39		base = int(ivbase)
40	}
41
42	return base, err
43}
44
45func convertMarshal(val reflect.Value) (bool, string, error) {
46	// Check first for the Marshaler interface
47	if val.Type().NumMethod() > 0 && val.CanInterface() {
48		if marshaler, ok := val.Interface().(Marshaler); ok {
49			ret, err := marshaler.MarshalFlag()
50			return true, ret, err
51		}
52	}
53
54	return false, "", nil
55}
56
57func convertToString(val reflect.Value, options reflect.StructTag) (string, error) {
58	if ok, ret, err := convertMarshal(val); ok {
59		return ret, err
60	}
61
62	tp := val.Type()
63
64	// Support for time.Duration
65	if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() {
66		stringer := val.Interface().(fmt.Stringer)
67		return stringer.String(), nil
68	}
69
70	switch tp.Kind() {
71	case reflect.String:
72		return val.String(), nil
73	case reflect.Bool:
74		if val.Bool() {
75			return "true", nil
76		}
77
78		return "false", nil
79	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
80		base, err := getBase(options, 10)
81
82		if err != nil {
83			return "", err
84		}
85
86		return strconv.FormatInt(val.Int(), base), nil
87	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
88		base, err := getBase(options, 10)
89
90		if err != nil {
91			return "", err
92		}
93
94		return strconv.FormatUint(val.Uint(), base), nil
95	case reflect.Float32, reflect.Float64:
96		return strconv.FormatFloat(val.Float(), 'g', -1, tp.Bits()), nil
97	case reflect.Slice:
98		if val.Len() == 0 {
99			return "", nil
100		}
101
102		ret := "["
103
104		for i := 0; i < val.Len(); i++ {
105			if i != 0 {
106				ret += ", "
107			}
108
109			item, err := convertToString(val.Index(i), options)
110
111			if err != nil {
112				return "", err
113			}
114
115			ret += item
116		}
117
118		return ret + "]", nil
119	case reflect.Map:
120		ret := "{"
121
122		for i, key := range val.MapKeys() {
123			if i != 0 {
124				ret += ", "
125			}
126
127			keyitem, err := convertToString(key, options)
128
129			if err != nil {
130				return "", err
131			}
132
133			item, err := convertToString(val.MapIndex(key), options)
134
135			if err != nil {
136				return "", err
137			}
138
139			ret += keyitem + ":" + item
140		}
141
142		return ret + "}", nil
143	case reflect.Ptr:
144		return convertToString(reflect.Indirect(val), options)
145	case reflect.Interface:
146		if !val.IsNil() {
147			return convertToString(val.Elem(), options)
148		}
149	}
150
151	return "", nil
152}
153
154func convertUnmarshal(val string, retval reflect.Value) (bool, error) {
155	if retval.Type().NumMethod() > 0 && retval.CanInterface() {
156		if unmarshaler, ok := retval.Interface().(Unmarshaler); ok {
157			return true, unmarshaler.UnmarshalFlag(val)
158		}
159	}
160
161	if retval.Type().Kind() != reflect.Ptr && retval.CanAddr() {
162		return convertUnmarshal(val, retval.Addr())
163	}
164
165	if retval.Type().Kind() == reflect.Interface && !retval.IsNil() {
166		return convertUnmarshal(val, retval.Elem())
167	}
168
169	return false, nil
170}
171
172func convert(val string, retval reflect.Value, options reflect.StructTag) error {
173	if ok, err := convertUnmarshal(val, retval); ok {
174		return err
175	}
176
177	tp := retval.Type()
178
179	// Support for time.Duration
180	if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() {
181		parsed, err := time.ParseDuration(val)
182
183		if err != nil {
184			return err
185		}
186
187		retval.SetInt(int64(parsed))
188		return nil
189	}
190
191	switch tp.Kind() {
192	case reflect.String:
193		retval.SetString(val)
194	case reflect.Bool:
195		if val == "" {
196			retval.SetBool(true)
197		} else {
198			b, err := strconv.ParseBool(val)
199
200			if err != nil {
201				return err
202			}
203
204			retval.SetBool(b)
205		}
206	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
207		base, err := getBase(options, 10)
208
209		if err != nil {
210			return err
211		}
212
213		parsed, err := strconv.ParseInt(val, base, tp.Bits())
214
215		if err != nil {
216			return err
217		}
218
219		retval.SetInt(parsed)
220	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
221		base, err := getBase(options, 10)
222
223		if err != nil {
224			return err
225		}
226
227		parsed, err := strconv.ParseUint(val, base, tp.Bits())
228
229		if err != nil {
230			return err
231		}
232
233		retval.SetUint(parsed)
234	case reflect.Float32, reflect.Float64:
235		parsed, err := strconv.ParseFloat(val, tp.Bits())
236
237		if err != nil {
238			return err
239		}
240
241		retval.SetFloat(parsed)
242	case reflect.Slice:
243		elemtp := tp.Elem()
244
245		elemvalptr := reflect.New(elemtp)
246		elemval := reflect.Indirect(elemvalptr)
247
248		if err := convert(val, elemval, options); err != nil {
249			return err
250		}
251
252		retval.Set(reflect.Append(retval, elemval))
253	case reflect.Map:
254		parts := strings.SplitN(val, ":", 2)
255
256		key := parts[0]
257		var value string
258
259		if len(parts) == 2 {
260			value = parts[1]
261		}
262
263		keytp := tp.Key()
264		keyval := reflect.New(keytp)
265
266		if err := convert(key, keyval, options); err != nil {
267			return err
268		}
269
270		valuetp := tp.Elem()
271		valueval := reflect.New(valuetp)
272
273		if err := convert(value, valueval, options); err != nil {
274			return err
275		}
276
277		if retval.IsNil() {
278			retval.Set(reflect.MakeMap(tp))
279		}
280
281		retval.SetMapIndex(reflect.Indirect(keyval), reflect.Indirect(valueval))
282	case reflect.Ptr:
283		if retval.IsNil() {
284			retval.Set(reflect.New(retval.Type().Elem()))
285		}
286
287		return convert(val, reflect.Indirect(retval), options)
288	case reflect.Interface:
289		if !retval.IsNil() {
290			return convert(val, retval.Elem(), options)
291		}
292	}
293
294	return nil
295}
296
297func isPrint(s string) bool {
298	for _, c := range s {
299		if !strconv.IsPrint(c) {
300			return false
301		}
302	}
303
304	return true
305}
306
307func quoteIfNeeded(s string) string {
308	if !isPrint(s) {
309		return strconv.Quote(s)
310	}
311
312	return s
313}
314
315func quoteIfNeededV(s []string) []string {
316	ret := make([]string, len(s))
317
318	for i, v := range s {
319		ret[i] = quoteIfNeeded(v)
320	}
321
322	return ret
323}
324
325func quoteV(s []string) []string {
326	ret := make([]string, len(s))
327
328	for i, v := range s {
329		ret[i] = strconv.Quote(v)
330	}
331
332	return ret
333}
334
335func unquoteIfPossible(s string) (string, error) {
336	if len(s) == 0 || s[0] != '"' {
337		return s, nil
338	}
339
340	return strconv.Unquote(s)
341}
342
343func wrapText(s string, l int, prefix string) string {
344	// Basic text wrapping of s at spaces to fit in l
345	var ret string
346
347	s = strings.TrimSpace(s)
348
349	for len(s) > l {
350		// Try to split on space
351		suffix := ""
352
353		pos := strings.LastIndex(s[:l], " ")
354
355		if pos < 0 {
356			pos = l - 1
357			suffix = "-\n"
358		}
359
360		if len(ret) != 0 {
361			ret += "\n" + prefix
362		}
363
364		ret += strings.TrimSpace(s[:pos]) + suffix
365		s = strings.TrimSpace(s[pos:])
366	}
367
368	if len(s) > 0 {
369		if len(ret) != 0 {
370			ret += "\n" + prefix
371		}
372
373		return ret + s
374	}
375
376	return ret
377}
378