| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // HEAVILY adapted from github.com/golang/appengine/datastore | 5 // HEAVILY adapted from github.com/golang/appengine/datastore |
| 6 | 6 |
| 7 package helper | 7 package rawdatastore |
| 8 | 8 |
| 9 import ( | 9 import ( |
| 10 "errors" | 10 "errors" |
| 11 "fmt" | 11 "fmt" |
| 12 "reflect" | 12 "reflect" |
| 13 "strconv" | 13 "strconv" |
| 14 "strings" | 14 "strings" |
| 15 "sync" | 15 "sync" |
| 16 "time" | 16 "time" |
| 17 "unicode" | 17 "unicode" |
| 18 | 18 |
| 19 "github.com/luci/gae" | 19 "github.com/luci/gae" |
| 20 "github.com/luci/gae/service/blobstore" |
| 20 ) | 21 ) |
| 21 | 22 |
| 22 // Entities with more than this many indexed properties will not be saved. | 23 // Entities with more than this many indexed properties will not be saved. |
| 23 const maxIndexedProperties = 20000 | 24 const maxIndexedProperties = 20000 |
| 24 | 25 |
| 25 var ( | |
| 26 typeOfDSKey = reflect.TypeOf((*gae.DSKey)(nil)).Elem() | |
| 27 typeOfDSPropertyConverter = reflect.TypeOf((*gae.DSPropertyConverter)(ni
l)).Elem() | |
| 28 typeOfGeoPoint = reflect.TypeOf(gae.DSGeoPoint{}) | |
| 29 typeOfTime = reflect.TypeOf(time.Time{}) | |
| 30 typeOfString = reflect.TypeOf("") | |
| 31 typeOfInt64 = reflect.TypeOf(int64(0)) | |
| 32 typeOfBool = reflect.TypeOf(true) | |
| 33 | |
| 34 valueOfnilDSKey = reflect.Zero(typeOfDSKey) | |
| 35 ) | |
| 36 | |
| 37 type structTag struct { | 26 type structTag struct { |
| 38 name string | 27 name string |
| 39 » idxSetting gae.IndexSetting | 28 » idxSetting IndexSetting |
| 40 isSlice bool | 29 isSlice bool |
| 41 substructCodec *structCodec | 30 substructCodec *structCodec |
| 42 convert bool | 31 convert bool |
| 43 metaVal interface{} | 32 metaVal interface{} |
| 44 canSet bool | 33 canSet bool |
| 45 } | 34 } |
| 46 | 35 |
| 47 type structCodec struct { | 36 type structCodec struct { |
| 48 byMeta map[string]int | 37 byMeta map[string]int |
| 49 byName map[string]int | 38 byName map[string]int |
| 50 byIndex []structTag | 39 byIndex []structTag |
| 51 hasSlice bool | 40 hasSlice bool |
| 52 problem error | 41 problem error |
| 53 } | 42 } |
| 54 | 43 |
| 55 type structPLS struct { | 44 type structPLS struct { |
| 56 o reflect.Value | 45 o reflect.Value |
| 57 c *structCodec | 46 c *structCodec |
| 58 } | 47 } |
| 59 | 48 |
| 60 var _ gae.DSPropertyLoadSaver = (*structPLS)(nil) | 49 var _ PropertyLoadSaver = (*structPLS)(nil) |
| 61 | 50 |
| 62 // typeMismatchReason returns a string explaining why the property p could not | 51 // typeMismatchReason returns a string explaining why the property p could not |
| 63 // be stored in an entity field of type v.Type(). | 52 // be stored in an entity field of type v.Type(). |
| 64 func typeMismatchReason(val interface{}, v reflect.Value) string { | 53 func typeMismatchReason(val interface{}, v reflect.Value) string { |
| 65 entityType := reflect.TypeOf(val) | 54 entityType := reflect.TypeOf(val) |
| 66 return fmt.Sprintf("type mismatch: %s versus %v", entityType, v.Type()) | 55 return fmt.Sprintf("type mismatch: %s versus %v", entityType, v.Type()) |
| 67 } | 56 } |
| 68 | 57 |
| 69 func (p *structPLS) Load(propMap gae.DSPropertyMap) error { | 58 func (p *structPLS) Load(propMap PropertyMap) error { |
| 70 if err := p.Problem(); err != nil { | 59 if err := p.Problem(); err != nil { |
| 71 return err | 60 return err |
| 72 } | 61 } |
| 73 | 62 |
| 74 convFailures := gae.MultiError(nil) | 63 convFailures := gae.MultiError(nil) |
| 75 | 64 |
| 76 t := reflect.Type(nil) | 65 t := reflect.Type(nil) |
| 77 for name, props := range propMap { | 66 for name, props := range propMap { |
| 78 multiple := len(props) > 1 | 67 multiple := len(props) > 1 |
| 79 for i, prop := range props { | 68 for i, prop := range props { |
| 80 if reason := loadInner(p.c, p.o, i, name, prop, multiple
); reason != "" { | 69 if reason := loadInner(p.c, p.o, i, name, prop, multiple
); reason != "" { |
| 81 if t == nil { | 70 if t == nil { |
| 82 t = p.o.Type() | 71 t = p.o.Type() |
| 83 } | 72 } |
| 84 » » » » convFailures = append(convFailures, &gae.ErrDSFi
eldMismatch{ | 73 » » » » convFailures = append(convFailures, &ErrFieldMis
match{ |
| 85 StructType: t, | 74 StructType: t, |
| 86 FieldName: name, | 75 FieldName: name, |
| 87 Reason: reason, | 76 Reason: reason, |
| 88 }) | 77 }) |
| 89 } | 78 } |
| 90 } | 79 } |
| 91 } | 80 } |
| 92 | 81 |
| 93 if len(convFailures) > 0 { | 82 if len(convFailures) > 0 { |
| 94 return convFailures | 83 return convFailures |
| 95 } | 84 } |
| 96 | 85 |
| 97 return nil | 86 return nil |
| 98 } | 87 } |
| 99 | 88 |
| 100 func loadInner(codec *structCodec, structValue reflect.Value, index int, name st
ring, p gae.DSProperty, requireSlice bool) string { | 89 func loadInner(codec *structCodec, structValue reflect.Value, index int, name st
ring, p Property, requireSlice bool) string { |
| 101 var v reflect.Value | 90 var v reflect.Value |
| 102 // Traverse a struct's struct-typed fields. | 91 // Traverse a struct's struct-typed fields. |
| 103 for { | 92 for { |
| 104 fieldIndex, ok := codec.byName[name] | 93 fieldIndex, ok := codec.byName[name] |
| 105 if !ok { | 94 if !ok { |
| 106 return "no such struct field" | 95 return "no such struct field" |
| 107 } | 96 } |
| 108 v = structValue.Field(fieldIndex) | 97 v = structValue.Field(fieldIndex) |
| 109 | 98 |
| 110 st := codec.byIndex[fieldIndex] | 99 st := codec.byIndex[fieldIndex] |
| (...skipping 10 matching lines...) Expand all Loading... |
| 121 } else { | 110 } else { |
| 122 structValue = v | 111 structValue = v |
| 123 } | 112 } |
| 124 // Strip the "I." from "I.X". | 113 // Strip the "I." from "I.X". |
| 125 name = name[len(st.name):] | 114 name = name[len(st.name):] |
| 126 codec = st.substructCodec | 115 codec = st.substructCodec |
| 127 } | 116 } |
| 128 | 117 |
| 129 doConversion := func(v reflect.Value) (string, bool) { | 118 doConversion := func(v reflect.Value) (string, bool) { |
| 130 a := v.Addr() | 119 a := v.Addr() |
| 131 » » if conv, ok := a.Interface().(gae.DSPropertyConverter); ok { | 120 » » if conv, ok := a.Interface().(PropertyConverter); ok { |
| 132 » » » err := conv.FromDSProperty(p) | 121 » » » err := conv.FromProperty(p) |
| 133 if err != nil { | 122 if err != nil { |
| 134 return err.Error(), true | 123 return err.Error(), true |
| 135 } | 124 } |
| 136 return "", true | 125 return "", true |
| 137 } | 126 } |
| 138 return "", false | 127 return "", false |
| 139 } | 128 } |
| 140 | 129 |
| 141 if ret, ok := doConversion(v); ok { | 130 if ret, ok := doConversion(v); ok { |
| 142 return ret | 131 return ret |
| 143 } | 132 } |
| 144 | 133 |
| 145 var slice reflect.Value | 134 var slice reflect.Value |
| 146 if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8
{ | 135 if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8
{ |
| 147 slice = v | 136 slice = v |
| 148 v = reflect.New(v.Type().Elem()).Elem() | 137 v = reflect.New(v.Type().Elem()).Elem() |
| 149 } else if requireSlice { | 138 } else if requireSlice { |
| 150 return "multiple-valued property requires a slice field type" | 139 return "multiple-valued property requires a slice field type" |
| 151 } | 140 } |
| 152 | 141 |
| 153 pVal := p.Value() | 142 pVal := p.Value() |
| 154 | 143 |
| 155 if ret, ok := doConversion(v); ok { | 144 if ret, ok := doConversion(v); ok { |
| 156 if ret != "" { | 145 if ret != "" { |
| 157 return ret | 146 return ret |
| 158 } | 147 } |
| 159 } else { | 148 } else { |
| 160 knd := v.Kind() | 149 knd := v.Kind() |
| 161 » » if v.Type().Implements(typeOfDSKey) { | 150 » » if v.Type().Implements(typeOfKey) { |
| 162 knd = reflect.Interface | 151 knd = reflect.Interface |
| 163 } | 152 } |
| 164 switch knd { | 153 switch knd { |
| 165 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, re
flect.Int64: | 154 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, re
flect.Int64: |
| 166 x, ok := pVal.(int64) | 155 x, ok := pVal.(int64) |
| 167 if !ok && pVal != nil { | 156 if !ok && pVal != nil { |
| 168 return typeMismatchReason(pVal, v) | 157 return typeMismatchReason(pVal, v) |
| 169 } | 158 } |
| 170 if v.OverflowInt(x) { | 159 if v.OverflowInt(x) { |
| 171 return fmt.Sprintf("value %v overflows struct fi
eld of type %v", x, v.Type()) | 160 return fmt.Sprintf("value %v overflows struct fi
eld of type %v", x, v.Type()) |
| 172 } | 161 } |
| 173 v.SetInt(x) | 162 v.SetInt(x) |
| 174 case reflect.Bool: | 163 case reflect.Bool: |
| 175 x, ok := pVal.(bool) | 164 x, ok := pVal.(bool) |
| 176 if !ok && pVal != nil { | 165 if !ok && pVal != nil { |
| 177 return typeMismatchReason(pVal, v) | 166 return typeMismatchReason(pVal, v) |
| 178 } | 167 } |
| 179 v.SetBool(x) | 168 v.SetBool(x) |
| 180 case reflect.String: | 169 case reflect.String: |
| 181 switch x := pVal.(type) { | 170 switch x := pVal.(type) { |
| 182 » » » case gae.BSKey: | 171 » » » case blobstore.Key: |
| 183 v.SetString(string(x)) | 172 v.SetString(string(x)) |
| 184 case string: | 173 case string: |
| 185 v.SetString(x) | 174 v.SetString(x) |
| 186 default: | 175 default: |
| 187 if pVal != nil { | 176 if pVal != nil { |
| 188 return typeMismatchReason(pVal, v) | 177 return typeMismatchReason(pVal, v) |
| 189 } | 178 } |
| 190 } | 179 } |
| 191 case reflect.Float32, reflect.Float64: | 180 case reflect.Float32, reflect.Float64: |
| 192 x, ok := pVal.(float64) | 181 x, ok := pVal.(float64) |
| 193 if !ok && pVal != nil { | 182 if !ok && pVal != nil { |
| 194 return typeMismatchReason(pVal, v) | 183 return typeMismatchReason(pVal, v) |
| 195 } | 184 } |
| 196 if v.OverflowFloat(x) { | 185 if v.OverflowFloat(x) { |
| 197 return fmt.Sprintf("value %v overflows struct fi
eld of type %v", x, v.Type()) | 186 return fmt.Sprintf("value %v overflows struct fi
eld of type %v", x, v.Type()) |
| 198 } | 187 } |
| 199 v.SetFloat(x) | 188 v.SetFloat(x) |
| 200 case reflect.Interface: | 189 case reflect.Interface: |
| 201 » » » x, ok := pVal.(gae.DSKey) | 190 » » » x, ok := pVal.(Key) |
| 202 if !ok && pVal != nil { | 191 if !ok && pVal != nil { |
| 203 return typeMismatchReason(pVal, v) | 192 return typeMismatchReason(pVal, v) |
| 204 } | 193 } |
| 205 if x != nil { | 194 if x != nil { |
| 206 v.Set(reflect.ValueOf(x)) | 195 v.Set(reflect.ValueOf(x)) |
| 207 } | 196 } |
| 208 case reflect.Struct: | 197 case reflect.Struct: |
| 209 switch v.Type() { | 198 switch v.Type() { |
| 210 case typeOfTime: | 199 case typeOfTime: |
| 211 x, ok := pVal.(time.Time) | 200 x, ok := pVal.(time.Time) |
| 212 if !ok && pVal != nil { | 201 if !ok && pVal != nil { |
| 213 return typeMismatchReason(pVal, v) | 202 return typeMismatchReason(pVal, v) |
| 214 } | 203 } |
| 215 v.Set(reflect.ValueOf(x)) | 204 v.Set(reflect.ValueOf(x)) |
| 216 case typeOfGeoPoint: | 205 case typeOfGeoPoint: |
| 217 » » » » x, ok := pVal.(gae.DSGeoPoint) | 206 » » » » x, ok := pVal.(GeoPoint) |
| 218 if !ok && pVal != nil { | 207 if !ok && pVal != nil { |
| 219 return typeMismatchReason(pVal, v) | 208 return typeMismatchReason(pVal, v) |
| 220 } | 209 } |
| 221 v.Set(reflect.ValueOf(x)) | 210 v.Set(reflect.ValueOf(x)) |
| 222 default: | 211 default: |
| 223 panic(fmt.Errorf("helper: impossible: %s", typeM
ismatchReason(pVal, v))) | 212 panic(fmt.Errorf("helper: impossible: %s", typeM
ismatchReason(pVal, v))) |
| 224 } | 213 } |
| 225 case reflect.Slice: | 214 case reflect.Slice: |
| 226 switch x := pVal.(type) { | 215 switch x := pVal.(type) { |
| 227 case []byte: | 216 case []byte: |
| 228 v.SetBytes(x) | 217 v.SetBytes(x) |
| 229 » » » case gae.DSByteString: | 218 » » » case ByteString: |
| 230 v.SetBytes([]byte(x)) | 219 v.SetBytes([]byte(x)) |
| 231 default: | 220 default: |
| 232 panic(fmt.Errorf("helper: impossible: %s", typeM
ismatchReason(pVal, v))) | 221 panic(fmt.Errorf("helper: impossible: %s", typeM
ismatchReason(pVal, v))) |
| 233 } | 222 } |
| 234 default: | 223 default: |
| 235 panic(fmt.Errorf("helper: impossible: %s", typeMismatchR
eason(pVal, v))) | 224 panic(fmt.Errorf("helper: impossible: %s", typeMismatchR
eason(pVal, v))) |
| 236 } | 225 } |
| 237 } | 226 } |
| 238 if slice.IsValid() { | 227 if slice.IsValid() { |
| 239 slice.Set(reflect.Append(slice, v)) | 228 slice.Set(reflect.Append(slice, v)) |
| 240 } | 229 } |
| 241 return "" | 230 return "" |
| 242 } | 231 } |
| 243 | 232 |
| 244 func (p *structPLS) Save(withMeta bool) (gae.DSPropertyMap, error) { | 233 func (p *structPLS) Save(withMeta bool) (PropertyMap, error) { |
| 245 size := len(p.c.byName) | 234 size := len(p.c.byName) |
| 246 if withMeta { | 235 if withMeta { |
| 247 size += len(p.c.byMeta) | 236 size += len(p.c.byMeta) |
| 248 } | 237 } |
| 249 » ret := make(gae.DSPropertyMap, size) | 238 » ret := make(PropertyMap, size) |
| 250 » if _, err := p.save(ret, "", gae.ShouldIndex); err != nil { | 239 » if _, err := p.save(ret, "", ShouldIndex); err != nil { |
| 251 return nil, err | 240 return nil, err |
| 252 } | 241 } |
| 253 if withMeta { | 242 if withMeta { |
| 254 for k := range p.c.byMeta { | 243 for k := range p.c.byMeta { |
| 255 val, err := p.GetMeta(k) | 244 val, err := p.GetMeta(k) |
| 256 if err != nil { | 245 if err != nil { |
| 257 return nil, err // TODO(riannucci): should these
be ignored? | 246 return nil, err // TODO(riannucci): should these
be ignored? |
| 258 } | 247 } |
| 259 » » » p := gae.DSProperty{} | 248 » » » p := Property{} |
| 260 » » » if err = p.SetValue(val, gae.NoIndex); err != nil { | 249 » » » if err = p.SetValue(val, NoIndex); err != nil { |
| 261 return nil, err | 250 return nil, err |
| 262 } | 251 } |
| 263 » » » ret["$"+k] = []gae.DSProperty{p} | 252 » » » ret["$"+k] = []Property{p} |
| 264 } | 253 } |
| 265 } | 254 } |
| 266 return ret, nil | 255 return ret, nil |
| 267 } | 256 } |
| 268 | 257 |
| 269 func (p *structPLS) save(propMap gae.DSPropertyMap, prefix string, is gae.IndexS
etting) (idxCount int, err error) { | 258 func (p *structPLS) save(propMap PropertyMap, prefix string, is IndexSetting) (i
dxCount int, err error) { |
| 270 if err = p.Problem(); err != nil { | 259 if err = p.Problem(); err != nil { |
| 271 return | 260 return |
| 272 } | 261 } |
| 273 | 262 |
| 274 » saveProp := func(name string, si gae.IndexSetting, v reflect.Value, st *
structTag) (err error) { | 263 » saveProp := func(name string, si IndexSetting, v reflect.Value, st *stru
ctTag) (err error) { |
| 275 if st.substructCodec != nil { | 264 if st.substructCodec != nil { |
| 276 count, err := (&structPLS{v, st.substructCodec}).save(pr
opMap, name, si) | 265 count, err := (&structPLS{v, st.substructCodec}).save(pr
opMap, name, si) |
| 277 if err == nil { | 266 if err == nil { |
| 278 idxCount += count | 267 idxCount += count |
| 279 if idxCount > maxIndexedProperties { | 268 if idxCount > maxIndexedProperties { |
| 280 err = errors.New("gae: too many indexed
properties") | 269 err = errors.New("gae: too many indexed
properties") |
| 281 } | 270 } |
| 282 } | 271 } |
| 283 return err | 272 return err |
| 284 } | 273 } |
| 285 | 274 |
| 286 » » prop := gae.DSProperty{} | 275 » » prop := Property{} |
| 287 if st.convert { | 276 if st.convert { |
| 288 » » » prop, err = v.Addr().Interface().(gae.DSPropertyConverte
r).ToDSProperty() | 277 » » » prop, err = v.Addr().Interface().(PropertyConverter).ToP
roperty() |
| 289 } else { | 278 } else { |
| 290 err = prop.SetValue(v.Interface(), si) | 279 err = prop.SetValue(v.Interface(), si) |
| 291 } | 280 } |
| 292 if err != nil { | 281 if err != nil { |
| 293 return err | 282 return err |
| 294 } | 283 } |
| 295 propMap[name] = append(propMap[name], prop) | 284 propMap[name] = append(propMap[name], prop) |
| 296 » » if prop.IndexSetting() == gae.ShouldIndex { | 285 » » if prop.IndexSetting() == ShouldIndex { |
| 297 idxCount++ | 286 idxCount++ |
| 298 if idxCount > maxIndexedProperties { | 287 if idxCount > maxIndexedProperties { |
| 299 return errors.New("gae: too many indexed propert
ies") | 288 return errors.New("gae: too many indexed propert
ies") |
| 300 } | 289 } |
| 301 } | 290 } |
| 302 return nil | 291 return nil |
| 303 } | 292 } |
| 304 | 293 |
| 305 for i, st := range p.c.byIndex { | 294 for i, st := range p.c.byIndex { |
| 306 if st.name == "-" { | 295 if st.name == "-" { |
| 307 continue | 296 continue |
| 308 } | 297 } |
| 309 name := st.name | 298 name := st.name |
| 310 if prefix != "" { | 299 if prefix != "" { |
| 311 name = prefix + name | 300 name = prefix + name |
| 312 } | 301 } |
| 313 v := p.o.Field(i) | 302 v := p.o.Field(i) |
| 314 is1 := is | 303 is1 := is |
| 315 » » if st.idxSetting == gae.NoIndex { | 304 » » if st.idxSetting == NoIndex { |
| 316 » » » is1 = gae.NoIndex | 305 » » » is1 = NoIndex |
| 317 } | 306 } |
| 318 if st.isSlice { | 307 if st.isSlice { |
| 319 for j := 0; j < v.Len(); j++ { | 308 for j := 0; j < v.Len(); j++ { |
| 320 if err = saveProp(name, is1, v.Index(j), &st); e
rr != nil { | 309 if err = saveProp(name, is1, v.Index(j), &st); e
rr != nil { |
| 321 return | 310 return |
| 322 } | 311 } |
| 323 } | 312 } |
| 324 } else { | 313 } else { |
| 325 if err = saveProp(name, is1, v, &st); err != nil { | 314 if err = saveProp(name, is1, v, &st); err != nil { |
| 326 return | 315 return |
| 327 } | 316 } |
| 328 } | 317 } |
| 329 } | 318 } |
| 330 return | 319 return |
| 331 } | 320 } |
| 332 | 321 |
| 333 func (p *structPLS) GetMeta(key string) (interface{}, error) { | 322 func (p *structPLS) GetMeta(key string) (interface{}, error) { |
| 334 if err := p.Problem(); err != nil { | 323 if err := p.Problem(); err != nil { |
| 335 return nil, err | 324 return nil, err |
| 336 } | 325 } |
| 337 idx, ok := p.c.byMeta[key] | 326 idx, ok := p.c.byMeta[key] |
| 338 if !ok { | 327 if !ok { |
| 339 » » return nil, gae.ErrDSMetaFieldUnset | 328 » » return nil, ErrMetaFieldUnset |
| 340 } | 329 } |
| 341 st := p.c.byIndex[idx] | 330 st := p.c.byIndex[idx] |
| 342 val := st.metaVal | 331 val := st.metaVal |
| 343 f := p.o.Field(idx) | 332 f := p.o.Field(idx) |
| 344 if st.canSet { | 333 if st.canSet { |
| 345 if !reflect.DeepEqual(reflect.Zero(f.Type()).Interface(), f.Inte
rface()) { | 334 if !reflect.DeepEqual(reflect.Zero(f.Type()).Interface(), f.Inte
rface()) { |
| 346 val = f.Interface() | 335 val = f.Interface() |
| 347 } | 336 } |
| 348 } | 337 } |
| 349 return val, nil | 338 return val, nil |
| 350 } | 339 } |
| 351 | 340 |
| 352 func (p *structPLS) SetMeta(key string, val interface{}) (err error) { | 341 func (p *structPLS) SetMeta(key string, val interface{}) (err error) { |
| 353 if err = p.Problem(); err != nil { | 342 if err = p.Problem(); err != nil { |
| 354 return | 343 return |
| 355 } | 344 } |
| 356 idx, ok := p.c.byMeta[key] | 345 idx, ok := p.c.byMeta[key] |
| 357 if !ok { | 346 if !ok { |
| 358 » » return gae.ErrDSMetaFieldUnset | 347 » » return ErrMetaFieldUnset |
| 359 } | 348 } |
| 360 if !p.c.byIndex[idx].canSet { | 349 if !p.c.byIndex[idx].canSet { |
| 361 return fmt.Errorf("gae/helper: cannot set meta %q: unexported fi
eld", key) | 350 return fmt.Errorf("gae/helper: cannot set meta %q: unexported fi
eld", key) |
| 362 } | 351 } |
| 363 p.o.Field(idx).Set(reflect.ValueOf(val)) | 352 p.o.Field(idx).Set(reflect.ValueOf(val)) |
| 364 return nil | 353 return nil |
| 365 } | 354 } |
| 366 | 355 |
| 367 func (p *structPLS) Problem() error { return p.c.problem } | 356 func (p *structPLS) Problem() error { return p.c.problem } |
| 368 | 357 |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 468 return | 457 return |
| 469 } | 458 } |
| 470 } | 459 } |
| 471 if !st.canSet { | 460 if !st.canSet { |
| 472 st.name = "-" | 461 st.name = "-" |
| 473 continue | 462 continue |
| 474 } | 463 } |
| 475 | 464 |
| 476 substructType := reflect.Type(nil) | 465 substructType := reflect.Type(nil) |
| 477 ft := f.Type | 466 ft := f.Type |
| 478 » » if reflect.PtrTo(ft).Implements(typeOfDSPropertyConverter) { | 467 » » if reflect.PtrTo(ft).Implements(typeOfPropertyConverter) { |
| 479 st.convert = true | 468 st.convert = true |
| 480 } else { | 469 } else { |
| 481 switch f.Type.Kind() { | 470 switch f.Type.Kind() { |
| 482 case reflect.Struct: | 471 case reflect.Struct: |
| 483 if ft != typeOfTime && ft != typeOfGeoPoint { | 472 if ft != typeOfTime && ft != typeOfGeoPoint { |
| 484 substructType = ft | 473 substructType = ft |
| 485 } | 474 } |
| 486 case reflect.Slice: | 475 case reflect.Slice: |
| 487 » » » » if reflect.PtrTo(ft.Elem()).Implements(typeOfDSP
ropertyConverter) { | 476 » » » » if reflect.PtrTo(ft.Elem()).Implements(typeOfPro
pertyConverter) { |
| 488 st.convert = true | 477 st.convert = true |
| 489 } else if ft.Elem().Kind() == reflect.Struct { | 478 } else if ft.Elem().Kind() == reflect.Struct { |
| 490 substructType = ft.Elem() | 479 substructType = ft.Elem() |
| 491 } | 480 } |
| 492 st.isSlice = ft.Elem().Kind() != reflect.Uint8 | 481 st.isSlice = ft.Elem().Kind() != reflect.Uint8 |
| 493 c.hasSlice = c.hasSlice || st.isSlice | 482 c.hasSlice = c.hasSlice || st.isSlice |
| 494 case reflect.Interface: | 483 case reflect.Interface: |
| 495 » » » » if ft != typeOfDSKey { | 484 » » » » if ft != typeOfKey { |
| 496 c.problem = me("field %q has non-concret
e interface type %s", | 485 c.problem = me("field %q has non-concret
e interface type %s", |
| 497 f.Name, f.Type) | 486 f.Name, f.Type) |
| 498 return | 487 return |
| 499 } | 488 } |
| 500 } | 489 } |
| 501 } | 490 } |
| 502 | 491 |
| 503 if substructType != nil { | 492 if substructType != nil { |
| 504 sub := getStructCodecLocked(substructType) | 493 sub := getStructCodecLocked(substructType) |
| 505 if sub.problem != nil { | 494 if sub.problem != nil { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 529 } | 518 } |
| 530 c.byName[absName] = i | 519 c.byName[absName] = i |
| 531 } | 520 } |
| 532 } else { | 521 } else { |
| 533 if !st.convert { // check the underlying static type of
the field | 522 if !st.convert { // check the underlying static type of
the field |
| 534 t := ft | 523 t := ft |
| 535 if st.isSlice { | 524 if st.isSlice { |
| 536 t = t.Elem() | 525 t = t.Elem() |
| 537 } | 526 } |
| 538 v := reflect.New(t).Elem().Interface() | 527 v := reflect.New(t).Elem().Interface() |
| 539 » » » » v, _ = gae.DSUpconvertUnderlyingType(v, t) | 528 » » » » v, _ = UpconvertUnderlyingType(v, t) |
| 540 » » » » if _, err := gae.DSPropertyTypeOf(v, false); err
!= nil { | 529 » » » » if _, err := PropertyTypeOf(v, false); err != ni
l { |
| 541 c.problem = me("field %q has invalid typ
e: %s", name, ft) | 530 c.problem = me("field %q has invalid typ
e: %s", name, ft) |
| 542 return | 531 return |
| 543 } | 532 } |
| 544 } | 533 } |
| 545 | 534 |
| 546 if _, ok := c.byName[name]; ok { | 535 if _, ok := c.byName[name]; ok { |
| 547 c.problem = me("struct tag has repeated property
name: %q", name) | 536 c.problem = me("struct tag has repeated property
name: %q", name) |
| 548 return | 537 return |
| 549 } | 538 } |
| 550 c.byName[name] = i | 539 c.byName[name] = i |
| 551 } | 540 } |
| 552 st.name = name | 541 st.name = name |
| 553 if opts == "noindex" { | 542 if opts == "noindex" { |
| 554 » » » st.idxSetting = gae.NoIndex | 543 » » » st.idxSetting = NoIndex |
| 555 } | 544 } |
| 556 } | 545 } |
| 557 if c.problem == errRecursiveStruct { | 546 if c.problem == errRecursiveStruct { |
| 558 c.problem = nil | 547 c.problem = nil |
| 559 } | 548 } |
| 560 return | 549 return |
| 561 } | 550 } |
| 562 | 551 |
| 563 func convertMeta(val string, t reflect.Type) (interface{}, error) { | 552 func convertMeta(val string, t reflect.Type) (interface{}, error) { |
| 564 switch t { | 553 switch t { |
| 565 case typeOfString: | 554 case typeOfString: |
| 566 return val, nil | 555 return val, nil |
| 567 case typeOfInt64: | 556 case typeOfInt64: |
| 568 if val == "" { | 557 if val == "" { |
| 569 return int64(0), nil | 558 return int64(0), nil |
| 570 } | 559 } |
| 571 return strconv.ParseInt(val, 10, 64) | 560 return strconv.ParseInt(val, 10, 64) |
| 572 } | 561 } |
| 573 return nil, fmt.Errorf("helper: meta field with bad type/value %s/%s", t
, val) | 562 return nil, fmt.Errorf("helper: meta field with bad type/value %s/%s", t
, val) |
| 574 } | 563 } |
| OLD | NEW |