Index: service/datastore/pls.go |
diff --git a/service/datastore/pls.go b/service/datastore/pls.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..bc0a3fe1fadc8595c81201b44533868be4a9edc8 |
--- /dev/null |
+++ b/service/datastore/pls.go |
@@ -0,0 +1,112 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package datastore |
+ |
+import ( |
+ "reflect" |
+) |
+ |
+// GetPLS resolves obj into a PropertyLoadSaver. |
iannucci
2015/08/03 04:00:34
Decided to give this a proper docstring
|
+// |
+// obj must be a non-nil pointer to a struct of some sort. |
+// |
+// By default, exported fields will be serialized to/from the datastore. If the |
+// field is not exported, it will be skipped by the serialization routines. |
+// |
+// If a field is of a non-supported type (see Property for the list of supported |
+// property types), the the resulting PropertyLoadSaver will have a non-nil |
+// Problem(). Other problems include duplicate field names (due to tagging), |
+// recursively defined structs, nested structures with multiple slices (e.g. |
+// slices of slices, either directly `[][]type` or indirectly `[]Embedded` where |
+// Embedded contains a slice.) |
+// |
+// GetPLS supports the following struct tag syntax: |
+// `gae:"fieldName[,noindex]"` -- an alternate fieldname for an exportable |
+// field. When the struct is serialized or deserialized, fieldName will be |
+// associated with the struct field instead of the field's Go name. This is |
+// useful when writing Go code which interfaces with appengine code written |
+// in other languages (like python) which use lowercase as their default |
+// datastore field names. |
+// |
+// A fieldName of "-" means that gae will ignore the field for all |
+// serialization/deserialization. |
+// |
+// if noindex is specified, then this field will not be indexed in the |
+// datastore, even if it was an otherwise indexable type. If fieldName is |
+// blank, and noindex is specifed, then fieldName will default to the |
+// field's actual name. |
+// |
+// `gae:"$metaKey[,<value>]` -- indicates a field is metadata. Metadata |
+// can be to control filter behavior, or to store key data when using |
+// the Interface.KeyForObject* methods. The supported field types are: |
+// - Key |
+// - int64 |
+// - string |
+// - Toggle (GetMeta and SetMeta treat the field as if it were bool) |
+// in addition, int64, string and Toggle allow setting a default value |
+// in the struct field tag (the "<value>" portion). |
+// |
+// Only exported fields allow SetMeta, but all fields of appropriate type |
+// allow tagged defaults. See Examples. |
+// |
+// Example "special" structure. This is supposed to be some sort of datastore |
+// singleton object. |
+// struct secretFoo { |
+// // _id and _kind are not exported, so setting their values will not be |
+// // reflected by GetMeta. |
+// _id int64 `gae:"$id,1"` |
+// _kind string `gae:"$kind,InternalFooSingleton"` |
+// |
+// // Value is exported, so can be read and written by the PropertyLoadSaver, |
+// // but secretFoo is shared with a python appengine module which has |
+// // stored this field as 'value' instead of 'Value'. |
+// Value int64 `gae:"value"` |
+// } |
+// |
+// Example "normal" structure that you might use in a go-only appengine app. |
+// struct User { |
+// ID string `gae:"$id"` |
+// // "kind" is automatically implied by the struct name: "User" |
+// // "parent" is nil... Users are root entities |
+// |
+// // 'Name' will serialized to the datastore in the field 'Name' |
+// Name string |
+// } |
+// |
+// struct Comment { |
+// ID int64 `gae:"$id"` |
+// // "kind" is automatically implied by the struct name: "Comment" |
+// |
+// // Parent will be enforced by the application to be a User key. |
+// Parent Key `gae:"$parent"` |
+// |
+// // 'Lines' will serialized to the datastore in the field 'Lines' |
+// Lines []string |
+// } |
+func GetPLS(obj interface{}) PropertyLoadSaver { |
+ v := reflect.ValueOf(obj) |
+ if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { |
+ return &structPLS{c: &structCodec{problem: ErrInvalidEntityType}} |
+ } |
+ if v.IsNil() { |
+ return &structPLS{c: &structCodec{problem: ErrInvalidEntityType}} |
+ } |
+ v = v.Elem() |
+ c := getCodec(v.Type()) |
+ return &structPLS{v, c} |
+} |
+ |
+func getCodec(structType reflect.Type) *structCodec { |
+ structCodecsMutex.RLock() |
+ c, ok := structCodecs[structType] |
+ structCodecsMutex.RUnlock() |
+ if ok { |
+ return c |
+ } |
+ |
+ structCodecsMutex.Lock() |
+ defer structCodecsMutex.Unlock() |
+ return getStructCodecLocked(structType) |
+} |