struct.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // Copyright 2015 Google Inc. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package search
  5. import (
  6. "fmt"
  7. "reflect"
  8. "strings"
  9. "sync"
  10. )
  11. // ErrFieldMismatch is returned when a field is to be loaded into a different
  12. // than the one it was stored from, or when a field is missing or unexported in
  13. // the destination struct.
  14. type ErrFieldMismatch struct {
  15. FieldName string
  16. Reason string
  17. }
  18. func (e *ErrFieldMismatch) Error() string {
  19. return fmt.Sprintf("search: cannot load field %q: %s", e.FieldName, e.Reason)
  20. }
  21. // ErrFacetMismatch is returned when a facet is to be loaded into a different
  22. // type than the one it was stored from, or when a field is missing or
  23. // unexported in the destination struct. StructType is the type of the struct
  24. // pointed to by the destination argument passed to Iterator.Next.
  25. type ErrFacetMismatch struct {
  26. StructType reflect.Type
  27. FacetName string
  28. Reason string
  29. }
  30. func (e *ErrFacetMismatch) Error() string {
  31. return fmt.Sprintf("search: cannot load facet %q into a %q: %s", e.FacetName, e.StructType, e.Reason)
  32. }
  33. // structCodec defines how to convert a given struct to/from a search document.
  34. type structCodec struct {
  35. // byIndex returns the struct tag for the i'th struct field.
  36. byIndex []structTag
  37. // fieldByName returns the index of the struct field for the given field name.
  38. fieldByName map[string]int
  39. // facetByName returns the index of the struct field for the given facet name,
  40. facetByName map[string]int
  41. }
  42. // structTag holds a structured version of each struct field's parsed tag.
  43. type structTag struct {
  44. name string
  45. facet bool
  46. }
  47. var (
  48. codecsMu sync.RWMutex
  49. codecs = map[reflect.Type]*structCodec{}
  50. )
  51. func loadCodec(t reflect.Type) (*structCodec, error) {
  52. codecsMu.RLock()
  53. codec, ok := codecs[t]
  54. codecsMu.RUnlock()
  55. if ok {
  56. return codec, nil
  57. }
  58. codecsMu.Lock()
  59. defer codecsMu.Unlock()
  60. if codec, ok := codecs[t]; ok {
  61. return codec, nil
  62. }
  63. codec = &structCodec{
  64. fieldByName: make(map[string]int),
  65. facetByName: make(map[string]int),
  66. }
  67. for i, I := 0, t.NumField(); i < I; i++ {
  68. f := t.Field(i)
  69. name, opts := f.Tag.Get("search"), ""
  70. if i := strings.Index(name, ","); i != -1 {
  71. name, opts = name[:i], name[i+1:]
  72. }
  73. // TODO(davidday): Support name=="-" as per datastore.
  74. if name == "" {
  75. name = f.Name
  76. } else if !validFieldName(name) {
  77. return nil, fmt.Errorf("search: struct tag has invalid field name: %q", name)
  78. }
  79. facet := opts == "facet"
  80. codec.byIndex = append(codec.byIndex, structTag{name: name, facet: facet})
  81. if facet {
  82. codec.facetByName[name] = i
  83. } else {
  84. codec.fieldByName[name] = i
  85. }
  86. }
  87. codecs[t] = codec
  88. return codec, nil
  89. }
  90. // structFLS adapts a struct to be a FieldLoadSaver.
  91. type structFLS struct {
  92. v reflect.Value
  93. codec *structCodec
  94. }
  95. func (s structFLS) Load(fields []Field, meta *DocumentMetadata) error {
  96. var err error
  97. for _, field := range fields {
  98. i, ok := s.codec.fieldByName[field.Name]
  99. if !ok {
  100. // Note the error, but keep going.
  101. err = &ErrFieldMismatch{
  102. FieldName: field.Name,
  103. Reason: "no such struct field",
  104. }
  105. continue
  106. }
  107. f := s.v.Field(i)
  108. if !f.CanSet() {
  109. // Note the error, but keep going.
  110. err = &ErrFieldMismatch{
  111. FieldName: field.Name,
  112. Reason: "cannot set struct field",
  113. }
  114. continue
  115. }
  116. v := reflect.ValueOf(field.Value)
  117. if ft, vt := f.Type(), v.Type(); ft != vt {
  118. err = &ErrFieldMismatch{
  119. FieldName: field.Name,
  120. Reason: fmt.Sprintf("type mismatch: %v for %v data", ft, vt),
  121. }
  122. continue
  123. }
  124. f.Set(v)
  125. }
  126. if meta == nil {
  127. return nil
  128. }
  129. for _, facet := range meta.Facets {
  130. i, ok := s.codec.facetByName[facet.Name]
  131. if !ok {
  132. // Note the error, but keep going.
  133. if err == nil {
  134. err = &ErrFacetMismatch{
  135. StructType: s.v.Type(),
  136. FacetName: facet.Name,
  137. Reason: "no matching field found",
  138. }
  139. }
  140. continue
  141. }
  142. f := s.v.Field(i)
  143. if !f.CanSet() {
  144. // Note the error, but keep going.
  145. if err == nil {
  146. err = &ErrFacetMismatch{
  147. StructType: s.v.Type(),
  148. FacetName: facet.Name,
  149. Reason: "unable to set unexported field of struct",
  150. }
  151. }
  152. continue
  153. }
  154. v := reflect.ValueOf(facet.Value)
  155. if ft, vt := f.Type(), v.Type(); ft != vt {
  156. if err == nil {
  157. err = &ErrFacetMismatch{
  158. StructType: s.v.Type(),
  159. FacetName: facet.Name,
  160. Reason: fmt.Sprintf("type mismatch: %v for %d data", ft, vt),
  161. }
  162. continue
  163. }
  164. }
  165. f.Set(v)
  166. }
  167. return err
  168. }
  169. func (s structFLS) Save() ([]Field, *DocumentMetadata, error) {
  170. fields := make([]Field, 0, len(s.codec.fieldByName))
  171. var facets []Facet
  172. for i, tag := range s.codec.byIndex {
  173. f := s.v.Field(i)
  174. if !f.CanSet() {
  175. continue
  176. }
  177. if tag.facet {
  178. facets = append(facets, Facet{Name: tag.name, Value: f.Interface()})
  179. } else {
  180. fields = append(fields, Field{Name: tag.name, Value: f.Interface()})
  181. }
  182. }
  183. return fields, &DocumentMetadata{Facets: facets}, nil
  184. }
  185. // newStructFLS returns a FieldLoadSaver for the struct pointer p.
  186. func newStructFLS(p interface{}) (FieldLoadSaver, error) {
  187. v := reflect.ValueOf(p)
  188. if v.Kind() != reflect.Ptr || v.IsNil() || v.Elem().Kind() != reflect.Struct {
  189. return nil, ErrInvalidDocumentType
  190. }
  191. codec, err := loadCodec(v.Elem().Type())
  192. if err != nil {
  193. return nil, err
  194. }
  195. return structFLS{v.Elem(), codec}, nil
  196. }
  197. func loadStructWithMeta(dst interface{}, f []Field, meta *DocumentMetadata) error {
  198. x, err := newStructFLS(dst)
  199. if err != nil {
  200. return err
  201. }
  202. return x.Load(f, meta)
  203. }
  204. func saveStructWithMeta(src interface{}) ([]Field, *DocumentMetadata, error) {
  205. x, err := newStructFLS(src)
  206. if err != nil {
  207. return nil, nil, err
  208. }
  209. return x.Save()
  210. }
  211. // LoadStruct loads the fields from f to dst. dst must be a struct pointer.
  212. func LoadStruct(dst interface{}, f []Field) error {
  213. return loadStructWithMeta(dst, f, nil)
  214. }
  215. // SaveStruct returns the fields from src as a slice of Field.
  216. // src must be a struct pointer.
  217. func SaveStruct(src interface{}) ([]Field, error) {
  218. f, _, err := saveStructWithMeta(src)
  219. return f, err
  220. }