Index: go/src/infra/gae/libs/gae/helper/datastore_impl.go |
diff --git a/go/src/infra/gae/libs/gae/helper/datastore_impl.go b/go/src/infra/gae/libs/gae/helper/datastore_impl.go |
index bfe066ea4871b9713e6ade287f3a284fe316d5e7..01a032e1530ab7e034c1b27bf0605b8c428abbf9 100644 |
--- a/go/src/infra/gae/libs/gae/helper/datastore_impl.go |
+++ b/go/src/infra/gae/libs/gae/helper/datastore_impl.go |
@@ -11,6 +11,7 @@ import ( |
"fmt" |
"infra/gae/libs/gae" |
Vadim Sh.
2015/07/14 18:55:43
nit: move into separate import block
iannucci
2015/07/14 22:45:48
Done.
|
"reflect" |
+ "strconv" |
"strings" |
"sync" |
"time" |
@@ -25,6 +26,8 @@ var ( |
typeOfDSPropertyConverter = reflect.TypeOf((*gae.DSPropertyConverter)(nil)).Elem() |
typeOfGeoPoint = reflect.TypeOf(gae.DSGeoPoint{}) |
typeOfTime = reflect.TypeOf(time.Time{}) |
+ typeOfString = reflect.TypeOf("") |
+ typeOfInt64 = reflect.TypeOf(int64(0)) |
valueOfnilDSKey = reflect.Zero(typeOfDSKey) |
) |
@@ -35,15 +38,16 @@ type structTag struct { |
isSlice bool |
substructCodec *structCodec |
convert bool |
- specialVal string |
+ metaVal interface{} |
+ metaIsString bool |
iannucci
2015/07/14 18:15:44
This metaIsString used to be useful... I'll take i
|
} |
type structCodec struct { |
- bySpecial map[string]int |
- byName map[string]int |
- byIndex []structTag |
- hasSlice bool |
- problem error |
+ byMeta map[string]int |
+ byName map[string]int |
+ byIndex []structTag |
+ hasSlice bool |
+ problem error |
} |
type structPLS struct { |
@@ -51,7 +55,7 @@ type structPLS struct { |
c *structCodec |
} |
-var _ gae.DSStructPLS = (*structPLS)(nil) |
+var _ gae.DSPropertyLoadSaver = (*structPLS)(nil) |
// typeMismatchReason returns a string explaining why the property p could not |
// be stored in an entity field of type v.Type(). |
@@ -60,22 +64,35 @@ func typeMismatchReason(val interface{}, v reflect.Value) string { |
return fmt.Sprintf("type mismatch: %s versus %v", entityType, v.Type()) |
} |
-func (p *structPLS) Load(propMap gae.DSPropertyMap) (convFailures []string, fatal error) { |
- if fatal = p.Problem(); fatal != nil { |
- return |
+func (p *structPLS) Load(propMap gae.DSPropertyMap) error { |
iannucci
2015/07/14 18:15:44
I changed this interface to only return one error,
|
+ if err := p.Problem(); err != nil { |
+ return err |
} |
- t := p.o.Type() |
+ convFailures := gae.MultiError(nil) |
+ |
+ 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 != "" { |
- convFailures = append(convFailures, fmt.Sprintf( |
- "cannot load field %q into a %q: %s", name, t, reason)) |
+ if t == nil { |
+ t = p.o.Type() |
+ } |
+ convFailures = append(convFailures, &gae.ErrDSFieldMismatch{ |
+ StructType: t, |
+ FieldName: name, |
+ Reason: reason, |
+ }) |
} |
} |
} |
- return |
+ |
+ if convFailures != nil { |
+ return convFailures |
+ } |
+ |
+ return nil |
} |
func loadInner(codec *structCodec, structValue reflect.Value, index int, name string, p gae.DSProperty, requireSlice bool) string { |
@@ -222,12 +239,29 @@ func loadInner(codec *structCodec, structValue reflect.Value, index int, name st |
return "" |
} |
-func (p *structPLS) Save() (gae.DSPropertyMap, error) { |
- ret := gae.DSPropertyMap{} |
+func (p *structPLS) Save(withMeta bool) (gae.DSPropertyMap, error) { |
iannucci
2015/07/14 18:15:44
withMeta was added so that the caller can ask for
|
+ size := len(p.c.byName) |
+ if withMeta { |
+ size += len(p.c.byMeta) |
+ } |
+ ret := make(gae.DSPropertyMap, size) |
idxCount := 0 |
if err := p.save(ret, &idxCount, "", false); err != nil { |
Vadim Sh.
2015/07/14 18:55:43
are you using pointers to return values from a fun
iannucci
2015/07/14 22:45:47
Yeah, I'm sorry... this is C+P from the SDK implem
|
return nil, err |
} |
+ if withMeta { |
+ for k := range p.c.byMeta { |
+ val, err := p.GetMeta(k) |
+ if err != nil { |
+ return nil, err // TODO(riannucci): should these be ignored? |
+ } |
+ p := gae.DSProperty{} |
+ if err = p.SetValue(val, true); err != nil { |
+ return nil, err |
+ } |
+ ret["$"+k] = []gae.DSProperty{p} |
+ } |
+ } |
return ret, nil |
} |
@@ -285,35 +319,37 @@ func (p *structPLS) save(propMap gae.DSPropertyMap, idxCount *int, prefix string |
return nil |
} |
-func (p *structPLS) GetSpecial(key string) (val string, current interface{}, err error) { |
- if err = p.Problem(); err != nil { |
- return |
+func (p *structPLS) GetMeta(key string) (interface{}, error) { |
iannucci
2015/07/14 18:15:43
"special" was renamed to "meta" to more clearly de
|
+ if err := p.Problem(); err != nil { |
+ return nil, err |
} |
- idx, ok := p.c.bySpecial[key] |
+ idx, ok := p.c.byMeta[key] |
if !ok { |
- err = gae.ErrDSSpecialFieldUnset |
- return |
+ return nil, gae.ErrDSMetaFieldUnset |
} |
- val = p.c.byIndex[idx].specialVal |
+ st := p.c.byIndex[idx] |
+ val := st.metaVal |
f := p.o.Field(idx) |
if f.CanSet() { |
- current = f.Interface() |
+ if !reflect.DeepEqual(reflect.Zero(f.Type()).Interface(), f.Interface()) { |
+ val = f.Interface() |
+ } |
} |
- return |
+ return val, nil |
} |
-func (p *structPLS) SetSpecial(key string, val interface{}) (err error) { |
+func (p *structPLS) SetMeta(key string, val interface{}) (err error) { |
if err = p.Problem(); err != nil { |
return |
} |
- idx, ok := p.c.bySpecial[key] |
+ idx, ok := p.c.byMeta[key] |
if !ok { |
- return gae.ErrDSSpecialFieldUnset |
+ return gae.ErrDSMetaFieldUnset |
} |
defer func() { |
pv := recover() |
Vadim Sh.
2015/07/14 18:55:43
nooooo!.. Is this the only way to catch errors her
dnj
2015/07/14 19:34:57
+1. IMO if some app is concerned with a panic in i
iannucci
2015/07/14 22:45:48
Fixed!
|
if pv != nil && err == nil { |
- err = fmt.Errorf("gae/helper: cannot set special %q: %s", key, pv) |
+ err = fmt.Errorf("gae/helper: cannot set meta %q: %s", key, pv) |
} |
}() |
p.o.Field(idx).Set(reflect.ValueOf(val)) |
@@ -372,17 +408,17 @@ func getStructCodecLocked(t reflect.Type) (c *structCodec) { |
} |
c = &structCodec{ |
- byIndex: make([]structTag, t.NumField()), |
- byName: make(map[string]int, t.NumField()), |
- bySpecial: make(map[string]int, t.NumField()), |
- problem: errRecursiveStruct, // we'll clear this later if it's not recursive |
+ byIndex: make([]structTag, t.NumField()), |
+ byName: make(map[string]int, t.NumField()), |
+ byMeta: make(map[string]int, t.NumField()), |
+ problem: errRecursiveStruct, // we'll clear this later if it's not recursive |
} |
defer func() { |
// If the codec has a problem, free up the indexes |
if c.problem != nil { |
c.byIndex = nil |
c.byName = nil |
- c.bySpecial = nil |
+ c.byMeta = nil |
} |
}() |
structCodecs[t] = c |
@@ -402,12 +438,18 @@ func getStructCodecLocked(t reflect.Type) (c *structCodec) { |
} |
case name[0] == '$': |
name = name[1:] |
- if _, ok := c.bySpecial[name]; ok { |
- c.problem = me("special field %q set multiple times", "$"+name) |
+ if _, ok := c.byMeta[name]; ok { |
+ c.problem = me("meta field %q set multiple times", "$"+name) |
return |
} |
- c.bySpecial[name] = i |
- st.specialVal = opts |
+ c.byMeta[name] = i |
+ mv, err := convertMeta(opts, f.Type) |
+ if err != nil { |
+ c.problem = me("meta field %q has bad type: %s", "$"+name, err) |
+ return |
+ } |
+ st.metaVal = mv |
+ st.metaIsString = f.Type.Kind() == reflect.String |
fallthrough |
case name == "-": |
st.name = "-" |
@@ -507,3 +549,16 @@ func getStructCodecLocked(t reflect.Type) (c *structCodec) { |
} |
return |
} |
+ |
+func convertMeta(val string, t reflect.Type) (interface{}, error) { |
+ switch t { |
Vadim Sh.
2015/07/14 18:55:43
what about boolean?
iannucci
2015/07/14 22:45:48
oh, right. I said supported that, didn't I? :D I'm
|
+ case typeOfString: |
+ return val, nil |
+ case typeOfInt64: |
+ if val == "" { |
+ return int64(0), nil |
+ } |
+ return strconv.ParseInt(val, 10, 64) |
+ } |
+ return nil, fmt.Errorf("helper: meta field with bad type %s", t) |
+} |