diff options
Diffstat (limited to 'vendor/google.golang.org/appengine/datastore/prop.go')
-rw-r--r-- | vendor/google.golang.org/appengine/datastore/prop.go | 118 |
1 files changed, 76 insertions, 42 deletions
diff --git a/vendor/google.golang.org/appengine/datastore/prop.go b/vendor/google.golang.org/appengine/datastore/prop.go index 1f50ac0..5cb2079 100644 --- a/vendor/google.golang.org/appengine/datastore/prop.go +++ b/vendor/google.golang.org/appengine/datastore/prop.go @@ -36,6 +36,7 @@ type Property struct { // - appengine.BlobKey // - appengine.GeoPoint // - []byte (up to 1 megabyte in length) + // - *Entity (representing a nested struct) // This set is smaller than the set of valid struct field types that the // datastore can load and save. A Property Value cannot be a slice (apart // from []byte); use multiple Properties instead. Also, a Value's type @@ -63,6 +64,13 @@ type Property struct { Multiple bool } +// An Entity is the value type for a nested struct. +// This type is only used for a Property's Value. +type Entity struct { + Key *Key + Properties []Property +} + // ByteString is a short byte slice (up to 1500 bytes) that can be indexed. type ByteString []byte @@ -119,25 +127,18 @@ func validPropertyName(name string) bool { return true } -// structTag is the parsed `datastore:"name,options"` tag of a struct field. -// If a field has no tag, or the tag has an empty name, then the structTag's -// name is just the field name. A "-" name means that the datastore ignores -// that field. -type structTag struct { - name string - noIndex bool -} - // structCodec describes how to convert a struct to and from a sequence of // properties. type structCodec struct { - // byIndex gives the structTag for the i'th field. - byIndex []structTag - // byName gives the field codec for the structTag with the given name. - byName map[string]fieldCodec + // fields gives the field codec for the structTag with the given name. + fields map[string]fieldCodec // hasSlice is whether a struct or any of its nested or embedded structs // has a slice-typed field (other than []byte). hasSlice bool + // keyField is the index of a *Key field with structTag __key__. + // This field is not relevant for the top level struct, only for + // nested structs. + keyField int // complete is whether the structCodec is complete. An incomplete // structCodec may be encountered when walking a recursive struct. complete bool @@ -146,8 +147,15 @@ type structCodec struct { // fieldCodec is a struct field's index and, if that struct field's type is // itself a struct, that substruct's structCodec. type fieldCodec struct { - index int - substructCodec *structCodec + // path is the index path to the field + path []int + noIndex bool + // omitEmpty indicates that the field should be omitted on save + // if empty. + omitEmpty bool + // structCodec is the codec fot the struct field at index 'path', + // or nil if the field is not a struct. + structCodec *structCodec } // structCodecs collects the structCodecs that have already been calculated. @@ -171,8 +179,10 @@ func getStructCodecLocked(t reflect.Type) (ret *structCodec, retErr error) { return c, nil } c = &structCodec{ - byIndex: make([]structTag, t.NumField()), - byName: make(map[string]fieldCodec), + fields: make(map[string]fieldCodec), + // We initialize keyField to -1 so that the zero-value is not + // misinterpreted as index 0. + keyField: -1, } // Add c to the structCodecs map before we are sure it is good. If t is @@ -185,22 +195,34 @@ func getStructCodecLocked(t reflect.Type) (ret *structCodec, retErr error) { } }() - for i := range c.byIndex { + for i := 0; i < t.NumField(); i++ { f := t.Field(i) + // Skip unexported fields. + // Note that if f is an anonymous, unexported struct field, + // we will promote its fields. + if f.PkgPath != "" && !f.Anonymous { + continue + } + tags := strings.Split(f.Tag.Get("datastore"), ",") name := tags[0] opts := make(map[string]bool) for _, t := range tags[1:] { opts[t] = true } - if name == "" { + switch { + case name == "": if !f.Anonymous { name = f.Name } - } else if name == "-" { - c.byIndex[i] = structTag{name: name} + case name == "-": continue - } else if !validPropertyName(name) { + case name == "__key__": + if f.Type != typeOfKeyPtr { + return nil, fmt.Errorf("datastore: __key__ field on struct %v is not a *datastore.Key", t) + } + c.keyField = i + case !validPropertyName(name): return nil, fmt.Errorf("datastore: struct tag has invalid property name: %q", name) } @@ -216,11 +238,10 @@ func getStructCodecLocked(t reflect.Type) (ret *structCodec, retErr error) { c.hasSlice = c.hasSlice || fIsSlice } + var sub *structCodec if substructType != nil && substructType != typeOfTime && substructType != typeOfGeoPoint { - if name != "" { - name = name + "." - } - sub, err := getStructCodecLocked(substructType) + var err error + sub, err = getStructCodecLocked(substructType) if err != nil { return nil, err } @@ -232,23 +253,35 @@ func getStructCodecLocked(t reflect.Type) (ret *structCodec, retErr error) { "datastore: flattening nested structs leads to a slice of slices: field %q", f.Name) } c.hasSlice = c.hasSlice || sub.hasSlice - for relName := range sub.byName { - absName := name + relName - if _, ok := c.byName[absName]; ok { - return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", absName) + // If f is an anonymous struct field, we promote the substruct's fields up to this level + // in the linked list of struct codecs. + if f.Anonymous { + for subname, subfield := range sub.fields { + if name != "" { + subname = name + "." + subname + } + if _, ok := c.fields[subname]; ok { + return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", subname) + } + c.fields[subname] = fieldCodec{ + path: append([]int{i}, subfield.path...), + noIndex: subfield.noIndex || opts["noindex"], + omitEmpty: subfield.omitEmpty, + structCodec: subfield.structCodec, + } } - c.byName[absName] = fieldCodec{index: i, substructCodec: sub} - } - } else { - if _, ok := c.byName[name]; ok { - return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", name) + continue } - c.byName[name] = fieldCodec{index: i} } - c.byIndex[i] = structTag{ - name: name, - noIndex: opts["noindex"], + if _, ok := c.fields[name]; ok { + return nil, fmt.Errorf("datastore: struct tag has repeated property name: %q", name) + } + c.fields[name] = fieldCodec{ + path: []int{i}, + noIndex: opts["noindex"], + omitEmpty: opts["omitempty"], + structCodec: sub, } } c.complete = true @@ -261,8 +294,9 @@ type structPLS struct { codec *structCodec } -// newStructPLS returns a PropertyLoadSaver for the struct pointer p. -func newStructPLS(p interface{}) (PropertyLoadSaver, error) { +// newStructPLS returns a structPLS, which implements the +// PropertyLoadSaver interface, for the struct pointer p. +func newStructPLS(p interface{}) (*structPLS, error) { v := reflect.ValueOf(p) if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { return nil, ErrInvalidEntityType @@ -272,7 +306,7 @@ func newStructPLS(p interface{}) (PropertyLoadSaver, error) { if err != nil { return nil, err } - return structPLS{v, codec}, nil + return &structPLS{v, codec}, nil } // LoadStruct loads the properties from p to dst. |