convert.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. // Copyright 2012 Jesse van den Kieboom. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package flags
  5. import (
  6. "fmt"
  7. "reflect"
  8. "strconv"
  9. "strings"
  10. "time"
  11. )
  12. // Marshaler is the interface implemented by types that can marshal themselves
  13. // to a string representation of the flag.
  14. type Marshaler interface {
  15. // MarshalFlag marshals a flag value to its string representation.
  16. MarshalFlag() (string, error)
  17. }
  18. // Unmarshaler is the interface implemented by types that can unmarshal a flag
  19. // argument to themselves. The provided value is directly passed from the
  20. // command line.
  21. type Unmarshaler interface {
  22. // UnmarshalFlag unmarshals a string value representation to the flag
  23. // value (which therefore needs to be a pointer receiver).
  24. UnmarshalFlag(value string) error
  25. }
  26. func getBase(options multiTag, base int) (int, error) {
  27. sbase := options.Get("base")
  28. var err error
  29. var ivbase int64
  30. if sbase != "" {
  31. ivbase, err = strconv.ParseInt(sbase, 10, 32)
  32. base = int(ivbase)
  33. }
  34. return base, err
  35. }
  36. func convertMarshal(val reflect.Value) (bool, string, error) {
  37. // Check first for the Marshaler interface
  38. if val.Type().NumMethod() > 0 && val.CanInterface() {
  39. if marshaler, ok := val.Interface().(Marshaler); ok {
  40. ret, err := marshaler.MarshalFlag()
  41. return true, ret, err
  42. }
  43. }
  44. return false, "", nil
  45. }
  46. func convertToString(val reflect.Value, options multiTag) (string, error) {
  47. if ok, ret, err := convertMarshal(val); ok {
  48. return ret, err
  49. }
  50. tp := val.Type()
  51. // Support for time.Duration
  52. if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() {
  53. stringer := val.Interface().(fmt.Stringer)
  54. return stringer.String(), nil
  55. }
  56. switch tp.Kind() {
  57. case reflect.String:
  58. return val.String(), nil
  59. case reflect.Bool:
  60. if val.Bool() {
  61. return "true", nil
  62. }
  63. return "false", nil
  64. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  65. base, _ := getBase(options, 10)
  66. return strconv.FormatInt(val.Int(), base), nil
  67. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  68. base, _ := getBase(options, 10)
  69. return strconv.FormatUint(val.Uint(), base), nil
  70. case reflect.Float32, reflect.Float64:
  71. return strconv.FormatFloat(val.Float(), 'g', -1, tp.Bits()), nil
  72. case reflect.Slice:
  73. if val.Len() == 0 {
  74. return "", nil
  75. }
  76. ret := "["
  77. for i := 0; i < val.Len(); i++ {
  78. if i != 0 {
  79. ret += ", "
  80. }
  81. item, err := convertToString(val.Index(i), options)
  82. if err != nil {
  83. return "", err
  84. }
  85. ret += item
  86. }
  87. return ret + "]", nil
  88. case reflect.Map:
  89. ret := "{"
  90. for i, key := range val.MapKeys() {
  91. if i != 0 {
  92. ret += ", "
  93. }
  94. item, err := convertToString(val.MapIndex(key), options)
  95. if err != nil {
  96. return "", err
  97. }
  98. ret += item
  99. }
  100. return ret + "}", nil
  101. case reflect.Ptr:
  102. return convertToString(reflect.Indirect(val), options)
  103. case reflect.Interface:
  104. if !val.IsNil() {
  105. return convertToString(val.Elem(), options)
  106. }
  107. }
  108. return "", nil
  109. }
  110. func convertUnmarshal(val string, retval reflect.Value) (bool, error) {
  111. if retval.Type().NumMethod() > 0 && retval.CanInterface() {
  112. if unmarshaler, ok := retval.Interface().(Unmarshaler); ok {
  113. return true, unmarshaler.UnmarshalFlag(val)
  114. }
  115. }
  116. if retval.Type().Kind() != reflect.Ptr && retval.CanAddr() {
  117. return convertUnmarshal(val, retval.Addr())
  118. }
  119. if retval.Type().Kind() == reflect.Interface && !retval.IsNil() {
  120. return convertUnmarshal(val, retval.Elem())
  121. }
  122. return false, nil
  123. }
  124. func convert(val string, retval reflect.Value, options multiTag) error {
  125. if ok, err := convertUnmarshal(val, retval); ok {
  126. return err
  127. }
  128. tp := retval.Type()
  129. // Support for time.Duration
  130. if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() {
  131. parsed, err := time.ParseDuration(val)
  132. if err != nil {
  133. return err
  134. }
  135. retval.SetInt(int64(parsed))
  136. return nil
  137. }
  138. switch tp.Kind() {
  139. case reflect.String:
  140. retval.SetString(val)
  141. case reflect.Bool:
  142. if val == "" {
  143. retval.SetBool(true)
  144. } else {
  145. b, err := strconv.ParseBool(val)
  146. if err != nil {
  147. return err
  148. }
  149. retval.SetBool(b)
  150. }
  151. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  152. base, err := getBase(options, 10)
  153. if err != nil {
  154. return err
  155. }
  156. parsed, err := strconv.ParseInt(val, base, tp.Bits())
  157. if err != nil {
  158. return err
  159. }
  160. retval.SetInt(parsed)
  161. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  162. base, err := getBase(options, 10)
  163. if err != nil {
  164. return err
  165. }
  166. parsed, err := strconv.ParseUint(val, base, tp.Bits())
  167. if err != nil {
  168. return err
  169. }
  170. retval.SetUint(parsed)
  171. case reflect.Float32, reflect.Float64:
  172. parsed, err := strconv.ParseFloat(val, tp.Bits())
  173. if err != nil {
  174. return err
  175. }
  176. retval.SetFloat(parsed)
  177. case reflect.Slice:
  178. elemtp := tp.Elem()
  179. elemvalptr := reflect.New(elemtp)
  180. elemval := reflect.Indirect(elemvalptr)
  181. if err := convert(val, elemval, options); err != nil {
  182. return err
  183. }
  184. retval.Set(reflect.Append(retval, elemval))
  185. case reflect.Map:
  186. parts := strings.SplitN(val, ":", 2)
  187. key := parts[0]
  188. var value string
  189. if len(parts) == 2 {
  190. value = parts[1]
  191. }
  192. keytp := tp.Key()
  193. keyval := reflect.New(keytp)
  194. if err := convert(key, keyval, options); err != nil {
  195. return err
  196. }
  197. valuetp := tp.Elem()
  198. valueval := reflect.New(valuetp)
  199. if err := convert(value, valueval, options); err != nil {
  200. return err
  201. }
  202. if retval.IsNil() {
  203. retval.Set(reflect.MakeMap(tp))
  204. }
  205. retval.SetMapIndex(reflect.Indirect(keyval), reflect.Indirect(valueval))
  206. case reflect.Ptr:
  207. if retval.IsNil() {
  208. retval.Set(reflect.New(retval.Type().Elem()))
  209. }
  210. return convert(val, reflect.Indirect(retval), options)
  211. case reflect.Interface:
  212. if !retval.IsNil() {
  213. return convert(val, retval.Elem(), options)
  214. }
  215. }
  216. return nil
  217. }
  218. func wrapText(s string, l int, prefix string) string {
  219. // Basic text wrapping of s at spaces to fit in l
  220. var ret string
  221. s = strings.TrimSpace(s)
  222. for len(s) > l {
  223. // Try to split on space
  224. suffix := ""
  225. pos := strings.LastIndex(s[:l], " ")
  226. if pos < 0 {
  227. pos = l - 1
  228. suffix = "-\n"
  229. }
  230. if len(ret) != 0 {
  231. ret += "\n" + prefix
  232. }
  233. ret += strings.TrimSpace(s[:pos]) + suffix
  234. s = strings.TrimSpace(s[pos:])
  235. }
  236. if len(s) > 0 {
  237. if len(ret) != 0 {
  238. ret += "\n" + prefix
  239. }
  240. return ret + s
  241. }
  242. return ret
  243. }