| Index: service/datastore/pls_impl.go
|
| diff --git a/service/datastore/pls_impl.go b/service/datastore/pls_impl.go
|
| index 9fab74f59e6a18364acbca48238867a03d0ff12c..5f7fe4740d06b5dd585f3444435340cdec8841bd 100644
|
| --- a/service/datastore/pls_impl.go
|
| +++ b/service/datastore/pls_impl.go
|
| @@ -27,12 +27,15 @@ type structTag struct {
|
| substructCodec *structCodec
|
| convert bool
|
| metaVal interface{}
|
| + isExtra bool
|
| canSet bool
|
| }
|
|
|
| type structCodec struct {
|
| - byMeta map[string]int
|
| - byName map[string]int
|
| + byMeta map[string]int
|
| + byName map[string]int
|
| + bySpecial map[string]int
|
| +
|
| byIndex []structTag
|
| hasSlice bool
|
| problem error
|
| @@ -55,19 +58,38 @@ func typeMismatchReason(val interface{}, v reflect.Value) string {
|
| func (p *structPLS) Load(propMap PropertyMap) error {
|
| convFailures := errors.MultiError(nil)
|
|
|
| + useExtra := false
|
| + extra := (*PropertyMap)(nil)
|
| + if i, ok := p.c.bySpecial["extra"]; ok {
|
| + useExtra = true
|
| + f := p.c.byIndex[i]
|
| + if f.canSet {
|
| + extra = p.o.Field(i).Addr().Interface().(*PropertyMap)
|
| + }
|
| + }
|
| t := reflect.Type(nil)
|
| for name, props := range propMap {
|
| multiple := len(props) > 1
|
| for i, prop := range props {
|
| if reason := loadInner(p.c, p.o, i, name, prop, multiple); reason != "" {
|
| - if t == nil {
|
| - t = p.o.Type()
|
| + if useExtra {
|
| + if extra != nil {
|
| + if *extra == nil {
|
| + *extra = make(PropertyMap, 1)
|
| + }
|
| + (*extra)[name] = props
|
| + }
|
| + break // go to the next property in propMap
|
| + } else {
|
| + if t == nil {
|
| + t = p.o.Type()
|
| + }
|
| + convFailures = append(convFailures, &ErrFieldMismatch{
|
| + StructType: t,
|
| + FieldName: name,
|
| + Reason: reason,
|
| + })
|
| }
|
| - convFailures = append(convFailures, &ErrFieldMismatch{
|
| - StructType: t,
|
| - FieldName: name,
|
| - Reason: reason,
|
| - })
|
| }
|
| }
|
| }
|
| @@ -253,7 +275,7 @@ func (p *structPLS) save(propMap PropertyMap, prefix string, is IndexSetting) (i
|
| }
|
|
|
| for i, st := range p.c.byIndex {
|
| - if st.name == "-" {
|
| + if st.name == "-" || st.isExtra {
|
| continue
|
| }
|
| name := st.name
|
| @@ -277,6 +299,17 @@ func (p *structPLS) save(propMap PropertyMap, prefix string, is IndexSetting) (i
|
| }
|
| }
|
| }
|
| +
|
| + if i, ok := p.c.bySpecial["extra"]; ok {
|
| + if p.c.byIndex[i].name != "-" {
|
| + for fullName, vals := range p.o.Field(i).Interface().(PropertyMap) {
|
| + if _, ok := propMap[fullName]; !ok {
|
| + propMap[fullName] = vals
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| return
|
| }
|
|
|
| @@ -419,9 +452,10 @@ func getStructCodecLocked(t reflect.Type) (c *structCodec) {
|
| }
|
|
|
| c = &structCodec{
|
| - byIndex: make([]structTag, t.NumField()),
|
| - byName: make(map[string]int, t.NumField()),
|
| - byMeta: make(map[string]int, t.NumField()),
|
| + byIndex: make([]structTag, t.NumField()),
|
| + byName: make(map[string]int, t.NumField()),
|
| + byMeta: make(map[string]int, t.NumField()),
|
| + bySpecial: make(map[string]int, 1),
|
|
|
| problem: errRecursiveStruct, // we'll clear this later if it's not recursive
|
| }
|
| @@ -446,6 +480,24 @@ func getStructCodecLocked(t reflect.Type) (c *structCodec) {
|
| name, opts = name[:i], name[i+1:]
|
| }
|
| st.canSet = f.PkgPath == "" // blank == exported
|
| + if opts == "extra" {
|
| + if _, ok := c.bySpecial["extra"]; ok {
|
| + c.problem = me("struct has multiple fields tagged as 'extra'")
|
| + return
|
| + }
|
| + if name != "" && name != "-" {
|
| + c.problem = me("struct 'extra' field has invalid name %s, expecing `` or `-`", name)
|
| + return
|
| + }
|
| + if ft != typeOfPropertyMap {
|
| + c.problem = me("struct 'extra' field has invalid type %s, expecing PropertyMap", ft)
|
| + return
|
| + }
|
| + st.isExtra = true
|
| + st.name = name
|
| + c.bySpecial["extra"] = i
|
| + continue
|
| + }
|
| st.convert = reflect.PtrTo(ft).Implements(typeOfPropertyConverter)
|
| switch {
|
| case name == "":
|
|
|