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