structs.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. // Package structs contains various utilities functions to work with structs.
  2. package structs
  3. import (
  4. "fmt"
  5. "reflect"
  6. )
  7. var (
  8. // DefaultTagName is the default tag name for struct fields which provides
  9. // a more granular to tweak certain structs. Lookup the necessary functions
  10. // for more info.
  11. DefaultTagName = "structs" // struct's field default tag name
  12. )
  13. // Struct encapsulates a struct type to provide several high level functions
  14. // around the struct.
  15. type Struct struct {
  16. raw interface{}
  17. value reflect.Value
  18. TagName string
  19. }
  20. // New returns a new *Struct with the struct s. It panics if the s's kind is
  21. // not struct.
  22. func New(s interface{}) *Struct {
  23. return &Struct{
  24. raw: s,
  25. value: strctVal(s),
  26. TagName: DefaultTagName,
  27. }
  28. }
  29. // Map converts the given struct to a map[string]interface{}, where the keys
  30. // of the map are the field names and the values of the map the associated
  31. // values of the fields. The default key string is the struct field name but
  32. // can be changed in the struct field's tag value. The "structs" key in the
  33. // struct's field tag value is the key name. Example:
  34. //
  35. // // Field appears in map as key "myName".
  36. // Name string `structs:"myName"`
  37. //
  38. // A tag value with the content of "-" ignores that particular field. Example:
  39. //
  40. // // Field is ignored by this package.
  41. // Field bool `structs:"-"`
  42. //
  43. // A tag value with the content of "string" uses the stringer to get the value. Example:
  44. //
  45. // // The value will be output of Animal's String() func.
  46. // // Map will panic if Animal does not implement String().
  47. // Field *Animal `structs:"field,string"`
  48. //
  49. // A tag value with the option of "flatten" used in a struct field is to flatten its fields
  50. // in the output map. Example:
  51. //
  52. // // The FieldStruct's fields will be flattened into the output map.
  53. // FieldStruct time.Time `structs:",flatten"`
  54. //
  55. // A tag value with the option of "omitnested" stops iterating further if the type
  56. // is a struct. Example:
  57. //
  58. // // Field is not processed further by this package.
  59. // Field time.Time `structs:"myName,omitnested"`
  60. // Field *http.Request `structs:",omitnested"`
  61. //
  62. // A tag value with the option of "omitempty" ignores that particular field if
  63. // the field value is empty. Example:
  64. //
  65. // // Field appears in map as key "myName", but the field is
  66. // // skipped if empty.
  67. // Field string `structs:"myName,omitempty"`
  68. //
  69. // // Field appears in map as key "Field" (the default), but
  70. // // the field is skipped if empty.
  71. // Field string `structs:",omitempty"`
  72. //
  73. // Note that only exported fields of a struct can be accessed, non exported
  74. // fields will be neglected.
  75. func (s *Struct) Map() map[string]interface{} {
  76. out := make(map[string]interface{})
  77. s.FillMap(out)
  78. return out
  79. }
  80. // FillMap is the same as Map. Instead of returning the output, it fills the
  81. // given map.
  82. func (s *Struct) FillMap(out map[string]interface{}) {
  83. if out == nil {
  84. return
  85. }
  86. fields := s.structFields()
  87. for _, field := range fields {
  88. name := field.Name
  89. val := s.value.FieldByName(name)
  90. isSubStruct := false
  91. var finalVal interface{}
  92. tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
  93. if tagName != "" {
  94. name = tagName
  95. }
  96. // if the value is a zero value and the field is marked as omitempty do
  97. // not include
  98. if tagOpts.Has("omitempty") {
  99. zero := reflect.Zero(val.Type()).Interface()
  100. current := val.Interface()
  101. if reflect.DeepEqual(current, zero) {
  102. continue
  103. }
  104. }
  105. if !tagOpts.Has("omitnested") {
  106. finalVal = s.nested(val)
  107. v := reflect.ValueOf(val.Interface())
  108. if v.Kind() == reflect.Ptr {
  109. v = v.Elem()
  110. }
  111. switch v.Kind() {
  112. case reflect.Map, reflect.Struct:
  113. isSubStruct = true
  114. }
  115. } else {
  116. finalVal = val.Interface()
  117. }
  118. if tagOpts.Has("string") {
  119. s, ok := val.Interface().(fmt.Stringer)
  120. if ok {
  121. out[name] = s.String()
  122. }
  123. continue
  124. }
  125. if isSubStruct && (tagOpts.Has("flatten")) {
  126. for k := range finalVal.(map[string]interface{}) {
  127. out[k] = finalVal.(map[string]interface{})[k]
  128. }
  129. } else {
  130. out[name] = finalVal
  131. }
  132. }
  133. }
  134. // Values converts the given s struct's field values to a []interface{}. A
  135. // struct tag with the content of "-" ignores the that particular field.
  136. // Example:
  137. //
  138. // // Field is ignored by this package.
  139. // Field int `structs:"-"`
  140. //
  141. // A value with the option of "omitnested" stops iterating further if the type
  142. // is a struct. Example:
  143. //
  144. // // Fields is not processed further by this package.
  145. // Field time.Time `structs:",omitnested"`
  146. // Field *http.Request `structs:",omitnested"`
  147. //
  148. // A tag value with the option of "omitempty" ignores that particular field and
  149. // is not added to the values if the field value is empty. Example:
  150. //
  151. // // Field is skipped if empty
  152. // Field string `structs:",omitempty"`
  153. //
  154. // Note that only exported fields of a struct can be accessed, non exported
  155. // fields will be neglected.
  156. func (s *Struct) Values() []interface{} {
  157. fields := s.structFields()
  158. var t []interface{}
  159. for _, field := range fields {
  160. val := s.value.FieldByName(field.Name)
  161. _, tagOpts := parseTag(field.Tag.Get(s.TagName))
  162. // if the value is a zero value and the field is marked as omitempty do
  163. // not include
  164. if tagOpts.Has("omitempty") {
  165. zero := reflect.Zero(val.Type()).Interface()
  166. current := val.Interface()
  167. if reflect.DeepEqual(current, zero) {
  168. continue
  169. }
  170. }
  171. if tagOpts.Has("string") {
  172. s, ok := val.Interface().(fmt.Stringer)
  173. if ok {
  174. t = append(t, s.String())
  175. }
  176. continue
  177. }
  178. if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
  179. // look out for embedded structs, and convert them to a
  180. // []interface{} to be added to the final values slice
  181. for _, embeddedVal := range Values(val.Interface()) {
  182. t = append(t, embeddedVal)
  183. }
  184. } else {
  185. t = append(t, val.Interface())
  186. }
  187. }
  188. return t
  189. }
  190. // Fields returns a slice of Fields. A struct tag with the content of "-"
  191. // ignores the checking of that particular field. Example:
  192. //
  193. // // Field is ignored by this package.
  194. // Field bool `structs:"-"`
  195. //
  196. // It panics if s's kind is not struct.
  197. func (s *Struct) Fields() []*Field {
  198. return getFields(s.value, s.TagName)
  199. }
  200. // Names returns a slice of field names. A struct tag with the content of "-"
  201. // ignores the checking of that particular field. Example:
  202. //
  203. // // Field is ignored by this package.
  204. // Field bool `structs:"-"`
  205. //
  206. // It panics if s's kind is not struct.
  207. func (s *Struct) Names() []string {
  208. fields := getFields(s.value, s.TagName)
  209. names := make([]string, len(fields))
  210. for i, field := range fields {
  211. names[i] = field.Name()
  212. }
  213. return names
  214. }
  215. func getFields(v reflect.Value, tagName string) []*Field {
  216. if v.Kind() == reflect.Ptr {
  217. v = v.Elem()
  218. }
  219. t := v.Type()
  220. var fields []*Field
  221. for i := 0; i < t.NumField(); i++ {
  222. field := t.Field(i)
  223. if tag := field.Tag.Get(tagName); tag == "-" {
  224. continue
  225. }
  226. f := &Field{
  227. field: field,
  228. value: v.FieldByName(field.Name),
  229. }
  230. fields = append(fields, f)
  231. }
  232. return fields
  233. }
  234. // Field returns a new Field struct that provides several high level functions
  235. // around a single struct field entity. It panics if the field is not found.
  236. func (s *Struct) Field(name string) *Field {
  237. f, ok := s.FieldOk(name)
  238. if !ok {
  239. panic("field not found")
  240. }
  241. return f
  242. }
  243. // FieldOk returns a new Field struct that provides several high level functions
  244. // around a single struct field entity. The boolean returns true if the field
  245. // was found.
  246. func (s *Struct) FieldOk(name string) (*Field, bool) {
  247. t := s.value.Type()
  248. field, ok := t.FieldByName(name)
  249. if !ok {
  250. return nil, false
  251. }
  252. return &Field{
  253. field: field,
  254. value: s.value.FieldByName(name),
  255. defaultTag: s.TagName,
  256. }, true
  257. }
  258. // IsZero returns true if all fields in a struct is a zero value (not
  259. // initialized) A struct tag with the content of "-" ignores the checking of
  260. // that particular field. Example:
  261. //
  262. // // Field is ignored by this package.
  263. // Field bool `structs:"-"`
  264. //
  265. // A value with the option of "omitnested" stops iterating further if the type
  266. // is a struct. Example:
  267. //
  268. // // Field is not processed further by this package.
  269. // Field time.Time `structs:"myName,omitnested"`
  270. // Field *http.Request `structs:",omitnested"`
  271. //
  272. // Note that only exported fields of a struct can be accessed, non exported
  273. // fields will be neglected. It panics if s's kind is not struct.
  274. func (s *Struct) IsZero() bool {
  275. fields := s.structFields()
  276. for _, field := range fields {
  277. val := s.value.FieldByName(field.Name)
  278. _, tagOpts := parseTag(field.Tag.Get(s.TagName))
  279. if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
  280. ok := IsZero(val.Interface())
  281. if !ok {
  282. return false
  283. }
  284. continue
  285. }
  286. // zero value of the given field, such as "" for string, 0 for int
  287. zero := reflect.Zero(val.Type()).Interface()
  288. // current value of the given field
  289. current := val.Interface()
  290. if !reflect.DeepEqual(current, zero) {
  291. return false
  292. }
  293. }
  294. return true
  295. }
  296. // HasZero returns true if a field in a struct is not initialized (zero value).
  297. // A struct tag with the content of "-" ignores the checking of that particular
  298. // field. Example:
  299. //
  300. // // Field is ignored by this package.
  301. // Field bool `structs:"-"`
  302. //
  303. // A value with the option of "omitnested" stops iterating further if the type
  304. // is a struct. Example:
  305. //
  306. // // Field is not processed further by this package.
  307. // Field time.Time `structs:"myName,omitnested"`
  308. // Field *http.Request `structs:",omitnested"`
  309. //
  310. // Note that only exported fields of a struct can be accessed, non exported
  311. // fields will be neglected. It panics if s's kind is not struct.
  312. func (s *Struct) HasZero() bool {
  313. fields := s.structFields()
  314. for _, field := range fields {
  315. val := s.value.FieldByName(field.Name)
  316. _, tagOpts := parseTag(field.Tag.Get(s.TagName))
  317. if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
  318. ok := HasZero(val.Interface())
  319. if ok {
  320. return true
  321. }
  322. continue
  323. }
  324. // zero value of the given field, such as "" for string, 0 for int
  325. zero := reflect.Zero(val.Type()).Interface()
  326. // current value of the given field
  327. current := val.Interface()
  328. if reflect.DeepEqual(current, zero) {
  329. return true
  330. }
  331. }
  332. return false
  333. }
  334. // Name returns the structs's type name within its package. For more info refer
  335. // to Name() function.
  336. func (s *Struct) Name() string {
  337. return s.value.Type().Name()
  338. }
  339. // structFields returns the exported struct fields for a given s struct. This
  340. // is a convenient helper method to avoid duplicate code in some of the
  341. // functions.
  342. func (s *Struct) structFields() []reflect.StructField {
  343. t := s.value.Type()
  344. var f []reflect.StructField
  345. for i := 0; i < t.NumField(); i++ {
  346. field := t.Field(i)
  347. // we can't access the value of unexported fields
  348. if field.PkgPath != "" {
  349. continue
  350. }
  351. // don't check if it's omitted
  352. if tag := field.Tag.Get(s.TagName); tag == "-" {
  353. continue
  354. }
  355. f = append(f, field)
  356. }
  357. return f
  358. }
  359. func strctVal(s interface{}) reflect.Value {
  360. v := reflect.ValueOf(s)
  361. // if pointer get the underlying element≤
  362. for v.Kind() == reflect.Ptr {
  363. v = v.Elem()
  364. }
  365. if v.Kind() != reflect.Struct {
  366. panic("not struct")
  367. }
  368. return v
  369. }
  370. // Map converts the given struct to a map[string]interface{}. For more info
  371. // refer to Struct types Map() method. It panics if s's kind is not struct.
  372. func Map(s interface{}) map[string]interface{} {
  373. return New(s).Map()
  374. }
  375. // FillMap is the same as Map. Instead of returning the output, it fills the
  376. // given map.
  377. func FillMap(s interface{}, out map[string]interface{}) {
  378. New(s).FillMap(out)
  379. }
  380. // Values converts the given struct to a []interface{}. For more info refer to
  381. // Struct types Values() method. It panics if s's kind is not struct.
  382. func Values(s interface{}) []interface{} {
  383. return New(s).Values()
  384. }
  385. // Fields returns a slice of *Field. For more info refer to Struct types
  386. // Fields() method. It panics if s's kind is not struct.
  387. func Fields(s interface{}) []*Field {
  388. return New(s).Fields()
  389. }
  390. // Names returns a slice of field names. For more info refer to Struct types
  391. // Names() method. It panics if s's kind is not struct.
  392. func Names(s interface{}) []string {
  393. return New(s).Names()
  394. }
  395. // IsZero returns true if all fields is equal to a zero value. For more info
  396. // refer to Struct types IsZero() method. It panics if s's kind is not struct.
  397. func IsZero(s interface{}) bool {
  398. return New(s).IsZero()
  399. }
  400. // HasZero returns true if any field is equal to a zero value. For more info
  401. // refer to Struct types HasZero() method. It panics if s's kind is not struct.
  402. func HasZero(s interface{}) bool {
  403. return New(s).HasZero()
  404. }
  405. // IsStruct returns true if the given variable is a struct or a pointer to
  406. // struct.
  407. func IsStruct(s interface{}) bool {
  408. v := reflect.ValueOf(s)
  409. if v.Kind() == reflect.Ptr {
  410. v = v.Elem()
  411. }
  412. // uninitialized zero value of a struct
  413. if v.Kind() == reflect.Invalid {
  414. return false
  415. }
  416. return v.Kind() == reflect.Struct
  417. }
  418. // Name returns the structs's type name within its package. It returns an
  419. // empty string for unnamed types. It panics if s's kind is not struct.
  420. func Name(s interface{}) string {
  421. return New(s).Name()
  422. }
  423. // nested retrieves recursively all types for the given value and returns the
  424. // nested value.
  425. func (s *Struct) nested(val reflect.Value) interface{} {
  426. var finalVal interface{}
  427. v := reflect.ValueOf(val.Interface())
  428. if v.Kind() == reflect.Ptr {
  429. v = v.Elem()
  430. }
  431. switch v.Kind() {
  432. case reflect.Struct:
  433. n := New(val.Interface())
  434. n.TagName = s.TagName
  435. m := n.Map()
  436. // do not add the converted value if there are no exported fields, ie:
  437. // time.Time
  438. if len(m) == 0 {
  439. finalVal = val.Interface()
  440. } else {
  441. finalVal = m
  442. }
  443. case reflect.Map:
  444. v := val.Type().Elem()
  445. if v.Kind() == reflect.Ptr {
  446. v = v.Elem()
  447. }
  448. // only iterate over struct types, ie: map[string]StructType,
  449. // map[string][]StructType,
  450. if v.Kind() == reflect.Struct ||
  451. (v.Kind() == reflect.Slice && v.Elem().Kind() == reflect.Struct) {
  452. m := make(map[string]interface{}, val.Len())
  453. for _, k := range val.MapKeys() {
  454. m[k.String()] = s.nested(val.MapIndex(k))
  455. }
  456. finalVal = m
  457. break
  458. }
  459. // TODO(arslan): should this be optional?
  460. finalVal = val.Interface()
  461. case reflect.Slice, reflect.Array:
  462. if val.Type().Kind() == reflect.Interface {
  463. finalVal = val.Interface()
  464. break
  465. }
  466. // TODO(arslan): should this be optional?
  467. // do not iterate of non struct types, just pass the value. Ie: []int,
  468. // []string, co... We only iterate further if it's a struct.
  469. // i.e []foo or []*foo
  470. if val.Type().Elem().Kind() != reflect.Struct &&
  471. !(val.Type().Elem().Kind() == reflect.Ptr &&
  472. val.Type().Elem().Elem().Kind() == reflect.Struct) {
  473. finalVal = val.Interface()
  474. break
  475. }
  476. slices := make([]interface{}, val.Len(), val.Len())
  477. for x := 0; x < val.Len(); x++ {
  478. slices[x] = s.nested(val.Index(x))
  479. }
  480. finalVal = slices
  481. default:
  482. finalVal = val.Interface()
  483. }
  484. return finalVal
  485. }