本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 line
8.4KB

  1. // Copyright 2018 Frank Schroeder. 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 properties
  5. import (
  6. "fmt"
  7. "reflect"
  8. "strconv"
  9. "strings"
  10. "time"
  11. )
  12. // Decode assigns property values to exported fields of a struct.
  13. //
  14. // Decode traverses v recursively and returns an error if a value cannot be
  15. // converted to the field type or a required value is missing for a field.
  16. //
  17. // The following type dependent decodings are used:
  18. //
  19. // String, boolean, numeric fields have the value of the property key assigned.
  20. // The property key name is the name of the field. A different key and a default
  21. // value can be set in the field's tag. Fields without default value are
  22. // required. If the value cannot be converted to the field type an error is
  23. // returned.
  24. //
  25. // time.Duration fields have the result of time.ParseDuration() assigned.
  26. //
  27. // time.Time fields have the vaule of time.Parse() assigned. The default layout
  28. // is time.RFC3339 but can be set in the field's tag.
  29. //
  30. // Arrays and slices of string, boolean, numeric, time.Duration and time.Time
  31. // fields have the value interpreted as a comma separated list of values. The
  32. // individual values are trimmed of whitespace and empty values are ignored. A
  33. // default value can be provided as a semicolon separated list in the field's
  34. // tag.
  35. //
  36. // Struct fields are decoded recursively using the field name plus "." as
  37. // prefix. The prefix (without dot) can be overridden in the field's tag.
  38. // Default values are not supported in the field's tag. Specify them on the
  39. // fields of the inner struct instead.
  40. //
  41. // Map fields must have a key of type string and are decoded recursively by
  42. // using the field's name plus ".' as prefix and the next element of the key
  43. // name as map key. The prefix (without dot) can be overridden in the field's
  44. // tag. Default values are not supported.
  45. //
  46. // Examples:
  47. //
  48. // // Field is ignored.
  49. // Field int `properties:"-"`
  50. //
  51. // // Field is assigned value of 'Field'.
  52. // Field int
  53. //
  54. // // Field is assigned value of 'myName'.
  55. // Field int `properties:"myName"`
  56. //
  57. // // Field is assigned value of key 'myName' and has a default
  58. // // value 15 if the key does not exist.
  59. // Field int `properties:"myName,default=15"`
  60. //
  61. // // Field is assigned value of key 'Field' and has a default
  62. // // value 15 if the key does not exist.
  63. // Field int `properties:",default=15"`
  64. //
  65. // // Field is assigned value of key 'date' and the date
  66. // // is in format 2006-01-02
  67. // Field time.Time `properties:"date,layout=2006-01-02"`
  68. //
  69. // // Field is assigned the non-empty and whitespace trimmed
  70. // // values of key 'Field' split by commas.
  71. // Field []string
  72. //
  73. // // Field is assigned the non-empty and whitespace trimmed
  74. // // values of key 'Field' split by commas and has a default
  75. // // value ["a", "b", "c"] if the key does not exist.
  76. // Field []string `properties:",default=a;b;c"`
  77. //
  78. // // Field is decoded recursively with "Field." as key prefix.
  79. // Field SomeStruct
  80. //
  81. // // Field is decoded recursively with "myName." as key prefix.
  82. // Field SomeStruct `properties:"myName"`
  83. //
  84. // // Field is decoded recursively with "Field." as key prefix
  85. // // and the next dotted element of the key as map key.
  86. // Field map[string]string
  87. //
  88. // // Field is decoded recursively with "myName." as key prefix
  89. // // and the next dotted element of the key as map key.
  90. // Field map[string]string `properties:"myName"`
  91. func (p *Properties) Decode(x interface{}) error {
  92. t, v := reflect.TypeOf(x), reflect.ValueOf(x)
  93. if t.Kind() != reflect.Ptr || v.Elem().Type().Kind() != reflect.Struct {
  94. return fmt.Errorf("not a pointer to struct: %s", t)
  95. }
  96. if err := dec(p, "", nil, nil, v); err != nil {
  97. return err
  98. }
  99. return nil
  100. }
  101. func dec(p *Properties, key string, def *string, opts map[string]string, v reflect.Value) error {
  102. t := v.Type()
  103. // value returns the property value for key or the default if provided.
  104. value := func() (string, error) {
  105. if val, ok := p.Get(key); ok {
  106. return val, nil
  107. }
  108. if def != nil {
  109. return *def, nil
  110. }
  111. return "", fmt.Errorf("missing required key %s", key)
  112. }
  113. // conv converts a string to a value of the given type.
  114. conv := func(s string, t reflect.Type) (val reflect.Value, err error) {
  115. var v interface{}
  116. switch {
  117. case isDuration(t):
  118. v, err = time.ParseDuration(s)
  119. case isTime(t):
  120. layout := opts["layout"]
  121. if layout == "" {
  122. layout = time.RFC3339
  123. }
  124. v, err = time.Parse(layout, s)
  125. case isBool(t):
  126. v, err = boolVal(s), nil
  127. case isString(t):
  128. v, err = s, nil
  129. case isFloat(t):
  130. v, err = strconv.ParseFloat(s, 64)
  131. case isInt(t):
  132. v, err = strconv.ParseInt(s, 10, 64)
  133. case isUint(t):
  134. v, err = strconv.ParseUint(s, 10, 64)
  135. default:
  136. return reflect.Zero(t), fmt.Errorf("unsupported type %s", t)
  137. }
  138. if err != nil {
  139. return reflect.Zero(t), err
  140. }
  141. return reflect.ValueOf(v).Convert(t), nil
  142. }
  143. // keydef returns the property key and the default value based on the
  144. // name of the struct field and the options in the tag.
  145. keydef := func(f reflect.StructField) (string, *string, map[string]string) {
  146. _key, _opts := parseTag(f.Tag.Get("properties"))
  147. var _def *string
  148. if d, ok := _opts["default"]; ok {
  149. _def = &d
  150. }
  151. if _key != "" {
  152. return _key, _def, _opts
  153. }
  154. return f.Name, _def, _opts
  155. }
  156. switch {
  157. case isDuration(t) || isTime(t) || isBool(t) || isString(t) || isFloat(t) || isInt(t) || isUint(t):
  158. s, err := value()
  159. if err != nil {
  160. return err
  161. }
  162. val, err := conv(s, t)
  163. if err != nil {
  164. return err
  165. }
  166. v.Set(val)
  167. case isPtr(t):
  168. return dec(p, key, def, opts, v.Elem())
  169. case isStruct(t):
  170. for i := 0; i < v.NumField(); i++ {
  171. fv := v.Field(i)
  172. fk, def, opts := keydef(t.Field(i))
  173. if !fv.CanSet() {
  174. return fmt.Errorf("cannot set %s", t.Field(i).Name)
  175. }
  176. if fk == "-" {
  177. continue
  178. }
  179. if key != "" {
  180. fk = key + "." + fk
  181. }
  182. if err := dec(p, fk, def, opts, fv); err != nil {
  183. return err
  184. }
  185. }
  186. return nil
  187. case isArray(t):
  188. val, err := value()
  189. if err != nil {
  190. return err
  191. }
  192. vals := split(val, ";")
  193. a := reflect.MakeSlice(t, 0, len(vals))
  194. for _, s := range vals {
  195. val, err := conv(s, t.Elem())
  196. if err != nil {
  197. return err
  198. }
  199. a = reflect.Append(a, val)
  200. }
  201. v.Set(a)
  202. case isMap(t):
  203. valT := t.Elem()
  204. m := reflect.MakeMap(t)
  205. for postfix := range p.FilterStripPrefix(key + ".").m {
  206. pp := strings.SplitN(postfix, ".", 2)
  207. mk, mv := pp[0], reflect.New(valT)
  208. if err := dec(p, key+"."+mk, nil, nil, mv); err != nil {
  209. return err
  210. }
  211. m.SetMapIndex(reflect.ValueOf(mk), mv.Elem())
  212. }
  213. v.Set(m)
  214. default:
  215. return fmt.Errorf("unsupported type %s", t)
  216. }
  217. return nil
  218. }
  219. // split splits a string on sep, trims whitespace of elements
  220. // and omits empty elements
  221. func split(s string, sep string) []string {
  222. var a []string
  223. for _, v := range strings.Split(s, sep) {
  224. if v = strings.TrimSpace(v); v != "" {
  225. a = append(a, v)
  226. }
  227. }
  228. return a
  229. }
  230. // parseTag parses a "key,k=v,k=v,..."
  231. func parseTag(tag string) (key string, opts map[string]string) {
  232. opts = map[string]string{}
  233. for i, s := range strings.Split(tag, ",") {
  234. if i == 0 {
  235. key = s
  236. continue
  237. }
  238. pp := strings.SplitN(s, "=", 2)
  239. if len(pp) == 1 {
  240. opts[pp[0]] = ""
  241. } else {
  242. opts[pp[0]] = pp[1]
  243. }
  244. }
  245. return key, opts
  246. }
  247. func isArray(t reflect.Type) bool { return t.Kind() == reflect.Array || t.Kind() == reflect.Slice }
  248. func isBool(t reflect.Type) bool { return t.Kind() == reflect.Bool }
  249. func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) }
  250. func isMap(t reflect.Type) bool { return t.Kind() == reflect.Map }
  251. func isPtr(t reflect.Type) bool { return t.Kind() == reflect.Ptr }
  252. func isString(t reflect.Type) bool { return t.Kind() == reflect.String }
  253. func isStruct(t reflect.Type) bool { return t.Kind() == reflect.Struct }
  254. func isTime(t reflect.Type) bool { return t == reflect.TypeOf(time.Time{}) }
  255. func isFloat(t reflect.Type) bool {
  256. return t.Kind() == reflect.Float32 || t.Kind() == reflect.Float64
  257. }
  258. func isInt(t reflect.Type) bool {
  259. return t.Kind() == reflect.Int || t.Kind() == reflect.Int8 || t.Kind() == reflect.Int16 || t.Kind() == reflect.Int32 || t.Kind() == reflect.Int64
  260. }
  261. func isUint(t reflect.Type) bool {
  262. return t.Kind() == reflect.Uint || t.Kind() == reflect.Uint8 || t.Kind() == reflect.Uint16 || t.Kind() == reflect.Uint32 || t.Kind() == reflect.Uint64
  263. }
上海开阖软件有限公司 沪ICP备12045867号-1