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 package gae | 5 package gae |
6 | 6 |
7 import ( | 7 import ( |
8 "errors" | 8 "errors" |
9 "fmt" | 9 "fmt" |
10 "math" | 10 "math" |
11 "reflect" | 11 "reflect" |
12 "time" | 12 "time" |
13 ) | 13 ) |
14 | 14 |
15 var ( | 15 var ( |
16 » // ErrDSSpecialFieldUnset is returned from DSStructPLS.{Get,Set}Special | 16 » // ErrDSMetaFieldUnset is returned from DSPropertyLoadSaver.{Get,Set}Met
a |
17 » // implementations when the specified special key isn't set on the struc
t at | 17 » // implementations when the specified meta key isn't set on the struct a
t |
18 // all. | 18 // all. |
19 » ErrDSSpecialFieldUnset = fmt.Errorf("gae: special field unset") | 19 » ErrDSMetaFieldUnset = fmt.Errorf("gae: meta field unset") |
20 ) | 20 ) |
21 | 21 |
22 var ( | 22 var ( |
23 typeOfBSKey = reflect.TypeOf(BSKey("")) | 23 typeOfBSKey = reflect.TypeOf(BSKey("")) |
24 typeOfBool = reflect.TypeOf(false) | 24 typeOfBool = reflect.TypeOf(false) |
25 typeOfByteSlice = reflect.TypeOf([]byte(nil)) | 25 typeOfByteSlice = reflect.TypeOf([]byte(nil)) |
26 typeOfDSByteString = reflect.TypeOf(DSByteString(nil)) | 26 typeOfDSByteString = reflect.TypeOf(DSByteString(nil)) |
27 typeOfDSGeoPoint = reflect.TypeOf(DSGeoPoint{}) | 27 typeOfDSGeoPoint = reflect.TypeOf(DSGeoPoint{}) |
28 typeOfDSKey = reflect.TypeOf((*DSKey)(nil)).Elem() | 28 typeOfDSKey = reflect.TypeOf((*DSKey)(nil)).Elem() |
29 typeOfFloat64 = reflect.TypeOf(float64(0)) | 29 typeOfFloat64 = reflect.TypeOf(float64(0)) |
30 typeOfInt64 = reflect.TypeOf(int64(0)) | 30 typeOfInt64 = reflect.TypeOf(int64(0)) |
31 typeOfString = reflect.TypeOf("") | 31 typeOfString = reflect.TypeOf("") |
32 typeOfTime = reflect.TypeOf(time.Time{}) | 32 typeOfTime = reflect.TypeOf(time.Time{}) |
33 ) | 33 ) |
34 | 34 |
35 var ( | 35 var ( |
36 minTime = time.Unix(int64(math.MinInt64)/1e6, (int64(math.MinInt64)%1e6)
*1e3) | 36 minTime = time.Unix(int64(math.MinInt64)/1e6, (int64(math.MinInt64)%1e6)
*1e3) |
37 maxTime = time.Unix(int64(math.MaxInt64)/1e6, (int64(math.MaxInt64)%1e6)
*1e3) | 37 maxTime = time.Unix(int64(math.MaxInt64)/1e6, (int64(math.MaxInt64)%1e6)
*1e3) |
38 ) | 38 ) |
39 | 39 |
| 40 type IndexSetting bool |
| 41 |
| 42 const ( |
| 43 // ShouldIndex is the default, which is why it must assume the zero valu
e, |
| 44 // even though it's werid :(. |
| 45 ShouldIndex IndexSetting = false |
| 46 NoIndex IndexSetting = true |
| 47 ) |
| 48 |
| 49 func (i IndexSetting) String() string { |
| 50 if i { |
| 51 return "NoIndex" |
| 52 } |
| 53 return "ShouldIndex" |
| 54 } |
| 55 |
40 // DSProperty is a value plus an indicator of whether the value should be | 56 // DSProperty is a value plus an indicator of whether the value should be |
41 // indexed. Name and Multiple are stored in the DSPropertyMap object. | 57 // indexed. Name and Multiple are stored in the DSPropertyMap object. |
42 type DSProperty struct { | 58 type DSProperty struct { |
43 » value interface{} | 59 » value interface{} |
44 » noIndex bool | 60 » indexSetting IndexSetting |
45 » propType DSPropertyType | 61 » propType DSPropertyType |
| 62 } |
| 63 |
| 64 // MkDSProperty makes a new indexed* DSProperty and returns it. If val is an |
| 65 // invalid value, this panics (so don't do it). If you want to handle the error |
| 66 // normally, use SetValue(..., ShouldIndex) instead. |
| 67 // |
| 68 // *indexed if val is not an unindexable type like []byte. |
| 69 func MkDSProperty(val interface{}) DSProperty { |
| 70 » ret := DSProperty{} |
| 71 » if err := ret.SetValue(val, ShouldIndex); err != nil { |
| 72 » » panic(err) |
| 73 » } |
| 74 » return ret |
| 75 } |
| 76 |
| 77 // MkDSPropertyNI makes a new DSProperty (with noindex set to true), and returns |
| 78 // it. If val is an invalid value, this panics (so don't do it). If you want to |
| 79 // handle the error normally, use SetValue(..., NoIndex) instead. |
| 80 func MkDSPropertyNI(val interface{}) DSProperty { |
| 81 » ret := DSProperty{} |
| 82 » if err := ret.SetValue(val, NoIndex); err != nil { |
| 83 » » panic(err) |
| 84 » } |
| 85 » return ret |
46 } | 86 } |
47 | 87 |
48 // DSPropertyConverter may be implemented by the pointer-to a struct field which | 88 // DSPropertyConverter may be implemented by the pointer-to a struct field which |
49 // is serialized by RawDatastore. Its ToDSProperty will be called on save, and | 89 // is serialized by RawDatastore. Its ToDSProperty will be called on save, and |
50 // it's FromDSProperty will be called on load (from datastore). The method may | 90 // it's FromDSProperty will be called on load (from datastore). The method may |
51 // do arbitrary computation, and if it encounters an error, may return it. This | 91 // do arbitrary computation, and if it encounters an error, may return it. This |
52 // error will be a fatal error (as defined by DSPropertyLoadSaver) for the | 92 // error will be a fatal error (as defined by DSPropertyLoadSaver) for the |
53 // struct conversion. | 93 // struct conversion. |
54 // | 94 // |
55 // Example: | 95 // Example: |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
229 } | 269 } |
230 } | 270 } |
231 return o, t | 271 return o, t |
232 } | 272 } |
233 | 273 |
234 // Value returns the current value held by this property. It's guaranteed to | 274 // Value returns the current value held by this property. It's guaranteed to |
235 // be a valid value type (i.e. `p.SetValue(p.Value(), true)` will never return | 275 // be a valid value type (i.e. `p.SetValue(p.Value(), true)` will never return |
236 // an error). | 276 // an error). |
237 func (p DSProperty) Value() interface{} { return p.value } | 277 func (p DSProperty) Value() interface{} { return p.value } |
238 | 278 |
239 // NoIndex says weather or not the datastore should create indicies for this | 279 // IndexSetting says weather or not the datastore should create indicies for |
240 // value. | 280 // this value. |
241 func (p DSProperty) NoIndex() bool { return p.noIndex } | 281 func (p DSProperty) IndexSetting() IndexSetting { return p.indexSetting } |
242 | 282 |
243 // Type is the DSPT* type of the data contained in Value(). | 283 // Type is the DSPT* type of the data contained in Value(). |
244 func (p DSProperty) Type() DSPropertyType { return p.propType } | 284 func (p DSProperty) Type() DSPropertyType { return p.propType } |
245 | 285 |
246 // SetValue sets the Value field of a DSProperty, and ensures that its value | 286 // SetValue sets the Value field of a DSProperty, and ensures that its value |
247 // conforms to the permissible types. That way, you're guaranteed that if you | 287 // conforms to the permissible types. That way, you're guaranteed that if you |
248 // have a DSProperty, its value is valid. | 288 // have a DSProperty, its value is valid. |
249 // | 289 // |
250 // value is the property value. The valid types are: | 290 // value is the property value. The valid types are: |
251 // - int64 | 291 // - int64 |
(...skipping 12 matching lines...) Expand all Loading... |
264 // must be explicitly on the list above; it is not sufficient for the | 304 // must be explicitly on the list above; it is not sufficient for the |
265 // underlying type to be on that list. For example, a Value of "type | 305 // underlying type to be on that list. For example, a Value of "type |
266 // myInt64 int64" is invalid. Smaller-width integers and floats are also | 306 // myInt64 int64" is invalid. Smaller-width integers and floats are also |
267 // invalid. Again, this is more restrictive than the set of valid struct | 307 // invalid. Again, this is more restrictive than the set of valid struct |
268 // field types. | 308 // field types. |
269 // | 309 // |
270 // A value may also be the nil interface value; this is equivalent to | 310 // A value may also be the nil interface value; this is equivalent to |
271 // Python's None but not directly representable by a Go struct. Loading | 311 // Python's None but not directly representable by a Go struct. Loading |
272 // a nil-valued property into a struct will set that field to the zero | 312 // a nil-valued property into a struct will set that field to the zero |
273 // value. | 313 // value. |
274 // | 314 func (p *DSProperty) SetValue(value interface{}, is IndexSetting) (err error) { |
275 // noIndex == false will attempt to set NoIndex to false as well. However, if | |
276 // value is an unindexable type, noIndex will be coerced to true automatically. | |
277 func (p *DSProperty) SetValue(value interface{}, noIndex bool) (err error) { | |
278 t := reflect.Type(nil) | 315 t := reflect.Type(nil) |
279 pt := DSPTNull | 316 pt := DSPTNull |
280 if value != nil { | 317 if value != nil { |
281 t = reflect.TypeOf(value) | 318 t = reflect.TypeOf(value) |
282 value, t = DSUpconvertUnderlyingType(value, t) | 319 value, t = DSUpconvertUnderlyingType(value, t) |
283 if pt, err = DSPropertyTypeOf(value, true); err != nil { | 320 if pt, err = DSPropertyTypeOf(value, true); err != nil { |
284 return | 321 return |
285 } | 322 } |
286 } | 323 } |
287 p.propType = pt | 324 p.propType = pt |
288 p.value = value | 325 p.value = value |
289 » p.noIndex = noIndex || t == typeOfByteSlice | 326 » p.indexSetting = is |
| 327 » if t == typeOfByteSlice { |
| 328 » » p.indexSetting = NoIndex |
| 329 » } |
290 return | 330 return |
291 } | 331 } |
292 | 332 |
293 // DSPropertyLoadSaver may be implemented by a user type, and RawDatastore will | 333 // DSPropertyLoadSaver may be implemented by a user type, and RawDatastore will |
294 // use this interface to serialize the type instead of trying to automatically | 334 // use this interface to serialize the type instead of trying to automatically |
295 // create a serialization codec for it with helper.GetStructPLS. | 335 // create a serialization codec for it with helper.GetPLS. |
296 type DSPropertyLoadSaver interface { | 336 type DSPropertyLoadSaver interface { |
297 » Load(DSPropertyMap) (convFailures []string, fatal error) | 337 » // Load takes the values from the given map and attempts to save them in
to |
298 » Save() (DSPropertyMap, error) | 338 » // the underlying object (usually a struct or a DSPropertyMap). If a fat
al |
299 } | 339 » // error occurs, it's returned via error. If non-fatal conversion errors |
| 340 » // occur, error will be a MultiError containing one or more ErrDSFieldMi
smatch |
| 341 » // objects. |
| 342 » Load(DSPropertyMap) error |
300 | 343 |
301 // DSStructPLS is a DSPropertyLoadSaver, but with some bonus features which only | 344 » // Save returns the current property as a DSPropertyMap. if withMeta is
true, |
302 // apply to user structs (instead of raw DSPropertyMap's). | 345 » // then the DSPropertyMap contains all the metadata (e.g. '$meta' fields
) |
303 type DSStructPLS interface { | 346 » // which was held by this DSPropertyLoadSaver. |
304 » DSPropertyLoadSaver | 347 » Save(withMeta bool) (DSPropertyMap, error) |
305 | 348 |
306 » // GetSpecial will get information about the struct field which has the | 349 » // GetMeta will get information about the field which has the struct tag
in |
307 » // struct tag in the form of `gae:"$<key>"`. | 350 » // the form of `gae:"$<key>[,<value>]?"`. |
| 351 » // |
| 352 » // string and int64 fields will return the <value> in the struct tag, |
| 353 » // converted to the appropriate type, if the field has the zero value. |
308 // | 354 // |
309 // Example: | 355 // Example: |
310 // type MyStruct struct { | 356 // type MyStruct struct { |
311 » // CoolField int `gae:"$id,cool"` | 357 » // CoolField int64 `gae:"$id,1"` |
312 // } | 358 // } |
313 » // val, current, err := helper.GetStructPLS(&MyStruct{10}).GetSpecial(
"id") | 359 » // val, err := helper.GetPLS(&MyStruct{}).GetMeta("id") |
314 » // // val == "cool" | 360 » // // val == 1 |
315 » // // current == 10 | |
316 // // err == nil | 361 // // err == nil |
317 » GetSpecial(key string) (val string, current interface{}, err error) | 362 » // |
| 363 » // val, err := helper.GetPLS(&MyStruct{10}).GetMeta("id") |
| 364 » // // val == 10 |
| 365 » // // err == nil |
| 366 » GetMeta(key string) (interface{}, error) |
318 | 367 |
319 » // SetSpecial allows you to set the current value of the special-keyed f
ield. | 368 » // SetMeta allows you to set the current value of the meta-keyed field. |
320 » SetSpecial(key string, val interface{}) error | 369 » SetMeta(key string, val interface{}) error |
321 | 370 |
322 » // Problem indicates that this StructPLS has a fatal problem. Usually th
is is | 371 » // Problem indicates that this PLS has a fatal problem. Usually this is |
323 // set when the underlying struct has recursion, invalid field types, ne
sted | 372 // set when the underlying struct has recursion, invalid field types, ne
sted |
324 // slices, etc. | 373 // slices, etc. |
325 Problem() error | 374 Problem() error |
326 } | 375 } |
327 | 376 |
328 // DSPropertyMap represents the contents of a datastore entity in a generic way. | 377 // DSPropertyMap represents the contents of a datastore entity in a generic way. |
329 // It maps from property name to a list of property values which correspond to | 378 // It maps from property name to a list of property values which correspond to |
330 // that property name. It is the spiritual successor to PropertyList from the | 379 // that property name. It is the spiritual successor to PropertyList from the |
331 // original SDK. | 380 // original SDK. |
| 381 // |
| 382 // DSPropertyMap may contain "meta" values, which are keyed with a '$' prefix. |
| 383 // Technically the datastore allows arbitrary property names, but all of the |
| 384 // SDKs go out of their way to try to make all property names valid programming |
| 385 // language tokens. Special values must correspond to a single DSProperty... |
| 386 // corresponding to 0 is equivalent to unset, and corresponding to >1 is an |
| 387 // error. So: |
| 388 // |
| 389 // { |
| 390 // "$id": {MkDSProperty(1)}, // GetProperty("id") -> 1, nil |
| 391 // "$foo": {}, // GetProperty("foo") -> nil, ErrDSMetaFieldUnset |
| 392 // // GetProperty("bar") -> nil, ErrDSMetaFieldUnset |
| 393 // "$meep": { |
| 394 // MkDSProperty("hi"), |
| 395 // MkDSProperty("there")}, // GetProperty("meep") -> nil, error! |
| 396 // } |
| 397 // |
| 398 // Additionally, Save returns a copy of the map with the meta keys omitted (e.g. |
| 399 // these keys are not going to be serialized to the datastore). |
332 type DSPropertyMap map[string][]DSProperty | 400 type DSPropertyMap map[string][]DSProperty |
333 | 401 |
334 var _ DSPropertyLoadSaver = (*DSPropertyMap)(nil) | 402 var _ DSPropertyLoadSaver = DSPropertyMap(nil) |
335 | 403 |
336 // Load implements DSPropertyLoadSaver.Load | 404 // Load implements DSPropertyLoadSaver.Load |
337 func (pm *DSPropertyMap) Load(props DSPropertyMap) (convErr []string, err error)
{ | 405 func (pm DSPropertyMap) Load(props DSPropertyMap) error { |
338 » if pm == nil { | 406 » for k, v := range props { |
339 » » return nil, errors.New("gae: nil DSPropertyMap") | 407 » » pm[k] = append(pm[k], v...) |
340 } | 408 } |
341 » if *pm == nil { | 409 » return nil |
342 » » *pm = make(DSPropertyMap, len(props)) | |
343 » } | |
344 » for k, v := range props { | |
345 » » (*pm)[k] = append((*pm)[k], v...) | |
346 » } | |
347 » return nil, nil | |
348 } | 410 } |
349 | 411 |
350 // Save implements DSPropertyLoadSaver.Save | 412 // Save implements DSPropertyLoadSaver.Save by returning a copy of the |
351 func (pm *DSPropertyMap) Save() (DSPropertyMap, error) { | 413 // current map data. |
352 » if pm == nil { | 414 func (pm DSPropertyMap) Save(withMeta bool) (DSPropertyMap, error) { |
353 » » return nil, errors.New("gae: nil DSPropertyMap") | 415 » if len(pm) == 0 { |
| 416 » » return DSPropertyMap{}, nil |
354 } | 417 } |
355 » return *pm, nil | 418 » ret := make(DSPropertyMap, len(pm)) |
| 419 » for k, v := range pm { |
| 420 » » if withMeta || len(k) == 0 || k[0] != '$' { |
| 421 » » » ret[k] = append(ret[k], v...) |
| 422 » » } |
| 423 » } |
| 424 » return ret, nil |
356 } | 425 } |
| 426 |
| 427 func (pm DSPropertyMap) GetMeta(key string) (interface{}, error) { |
| 428 v, ok := pm["$"+key] |
| 429 if !ok || len(v) == 0 { |
| 430 return nil, ErrDSMetaFieldUnset |
| 431 } |
| 432 if len(v) > 1 { |
| 433 return nil, errors.New("gae: too many values for Meta key") |
| 434 } |
| 435 return v[0].Value(), nil |
| 436 } |
| 437 |
| 438 func (pm DSPropertyMap) SetMeta(key string, val interface{}) error { |
| 439 prop := DSProperty{} |
| 440 if err := prop.SetValue(val, NoIndex); err != nil { |
| 441 return err |
| 442 } |
| 443 pm["$"+key] = []DSProperty{prop} |
| 444 return nil |
| 445 } |
| 446 |
| 447 func (pm DSPropertyMap) Problem() error { |
| 448 return nil |
| 449 } |
OLD | NEW |