Index: service/datastore/multiarg.go |
diff --git a/service/datastore/multiarg.go b/service/datastore/multiarg.go |
index 26d07b3f0fa8c160c12d38911a917bc2e9328f64..009c3f178b6431b56d7bd8b910933ff27c9e6a63 100644 |
--- a/service/datastore/multiarg.go |
+++ b/service/datastore/multiarg.go |
@@ -12,12 +12,29 @@ import ( |
) |
type multiArgType struct { |
- getKey func(aid, ns string, slot reflect.Value) (*Key, error) |
- getPM func(slot reflect.Value) (PropertyMap, error) |
- getMetaPM func(slot reflect.Value) PropertyMap |
- setPM func(slot reflect.Value, pm PropertyMap) error |
- setKey func(slot reflect.Value, k *Key) |
- newElem func() reflect.Value |
+ getMGS func(slot reflect.Value) MetaGetterSetter |
+ getPLS func(slot reflect.Value) PropertyLoadSaver |
+ newElem func() reflect.Value |
+} |
+ |
+func (mat *multiArgType) getKey(aid, ns string, slot reflect.Value) (*Key, error) { |
+ return newKeyObjErr(aid, ns, mat.getMGS(slot)) |
+} |
+ |
+func (mat *multiArgType) getPM(slot reflect.Value) (PropertyMap, error) { |
+ return mat.getPLS(slot).Save(true) |
+} |
+ |
+func (mat *multiArgType) getMetaPM(slot reflect.Value) PropertyMap { |
+ return mat.getMGS(slot).GetAllMeta() |
+} |
+ |
+func (mat *multiArgType) setPM(slot reflect.Value, pm PropertyMap) error { |
+ return mat.getPLS(slot).Load(pm) |
+} |
+ |
+func (mat *multiArgType) setKey(slot reflect.Value, k *Key) { |
+ PopulateKey(mat.getMGS(slot), k) |
} |
// parseArg checks that et is of type S, *S, I, P or *P, for some |
@@ -30,85 +47,120 @@ type multiArgType struct { |
// If keysOnly is true, a read-only key extraction multiArgType will be |
// returned if et is a *Key. |
func parseArg(et reflect.Type, keysOnly bool) *multiArgType { |
+ var mat multiArgType |
+ |
if keysOnly && et == typeOfKey { |
- return multiArgTypeKeyExtraction() |
+ mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return &keyExtractionMGS{key: slot.Interface().(*Key)} } |
+ return &mat |
} |
- if et.Kind() == reflect.Interface { |
- return multiArgTypeInterface() |
+ // If we do identify a structCodec for this type, retain it so we don't |
+ // resolve it multiple times. |
+ var codec *structCodec |
+ initCodec := func(t reflect.Type) *structCodec { |
+ if codec == nil { |
+ codec = getCodec(t) |
+ } |
+ return codec |
} |
- // If a map/chan type implements an interface, its pointer is also considered |
- // to implement that interface. |
- // |
- // In this case, we have special pointer-to-map/chan logic in multiArgTypePLS. |
- if et.Implements(typeOfPropertyLoadSaver) { |
- return multiArgTypePLS(et) |
- } |
- if reflect.PtrTo(et).Implements(typeOfPropertyLoadSaver) { |
- return multiArgTypePLSPtr(et) |
- } |
+ // Fill in MetaGetterSetter functions. |
+ switch { |
+ case et.Implements(typeOfMetaGetterSetter): |
+ // MGS |
+ mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return slot.Interface().(MetaGetterSetter) } |
- switch et.Kind() { |
- case reflect.Ptr: |
- if et.Elem().Kind() == reflect.Struct { |
- return multiArgTypeStructPtr(et) |
- } |
+ case reflect.PtrTo(et).Implements(typeOfMetaGetterSetter): |
+ // *MGS |
+ mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return slot.Addr().Interface().(MetaGetterSetter) } |
- case reflect.Struct: |
- return multiArgTypeStruct(et) |
- } |
+ default: |
+ switch et.Kind() { |
+ case reflect.Interface: |
+ // I |
+ mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return getMGS(slot.Elem().Interface()) } |
+ |
+ case reflect.Ptr: |
+ // *S |
+ if et.Elem().Kind() != reflect.Struct { |
+ // Not a struct pointer. |
+ return nil |
+ } |
- return nil |
-} |
+ initCodec(et.Elem()) |
+ mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return &structPLS{slot.Elem(), codec, nil} } |
-// parseMultiArg checks that v has type []S, []*S, []I, []P or []*P, for some |
-// struct type S, for some interface type I, or some non-interface non-pointer |
-// type P such that P or *P implements PropertyLoadSaver. |
-func mustParseMultiArg(et reflect.Type) *multiArgType { |
- if et.Kind() != reflect.Slice { |
- panic(fmt.Errorf("invalid argument type: expected slice, got %s", et)) |
+ case reflect.Struct: |
+ // S |
+ initCodec(et) |
+ mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return &structPLS{slot, codec, nil} } |
+ |
+ default: |
+ // Don't know how to get MGS for this type. |
+ return nil |
+ } |
} |
- return mustParseArg(et.Elem()) |
-} |
-func mustParseArg(et reflect.Type) *multiArgType { |
- if mat := parseArg(et, false); mat != nil { |
- return mat |
+ // Fill in PropertyLoadSaver functions. |
+ switch { |
+ case et.Implements(typeOfPropertyLoadSaver): |
+ // PLS |
+ mat.getPLS = func(slot reflect.Value) PropertyLoadSaver { return slot.Interface().(PropertyLoadSaver) } |
+ |
+ case reflect.PtrTo(et).Implements(typeOfPropertyLoadSaver): |
+ // *PLS |
+ mat.getPLS = func(slot reflect.Value) PropertyLoadSaver { return slot.Addr().Interface().(PropertyLoadSaver) } |
+ |
+ default: |
+ switch et.Kind() { |
+ case reflect.Interface: |
+ // I |
+ mat.getPLS = func(slot reflect.Value) PropertyLoadSaver { |
+ obj := slot.Elem().Interface() |
+ if pls, ok := obj.(PropertyLoadSaver); ok { |
+ return pls |
+ } |
+ return GetPLS(obj) |
+ } |
+ |
+ case reflect.Ptr: |
+ // *S |
+ if et.Elem().Kind() != reflect.Struct { |
+ // Not a struct pointer. |
+ return nil |
+ } |
+ initCodec(et.Elem()) |
+ mat.getPLS = func(slot reflect.Value) PropertyLoadSaver { return &structPLS{slot.Elem(), codec, nil} } |
+ |
+ case reflect.Struct: |
+ // S |
+ initCodec(et) |
+ mat.getPLS = func(slot reflect.Value) PropertyLoadSaver { return &structPLS{slot, codec, nil} } |
+ |
+ default: |
+ // Don't know how to get PLS for this type. |
+ return nil |
+ } |
} |
- panic(fmt.Errorf("invalid argument type: %s is not a PLS or pointer-to-struct", et)) |
-} |
-// multiArgTypePLS handles the case where et implements PropertyLoadSaver. |
-// |
-// This handles the special case of pointer-to-map and pointer-to-chan ( |
-// see parseArg). |
-func multiArgTypePLS(et reflect.Type) *multiArgType { |
- ret := multiArgType{ |
- getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { |
- return newKeyObjErr(aid, ns, getMGS(slot.Interface())) |
- }, |
- getPM: func(slot reflect.Value) (PropertyMap, error) { |
- return slot.Interface().(PropertyLoadSaver).Save(true) |
- }, |
- getMetaPM: func(slot reflect.Value) PropertyMap { |
- return getMGS(slot.Interface()).GetAllMeta() |
- }, |
- setPM: func(slot reflect.Value, pm PropertyMap) error { |
- return slot.Interface().(PropertyLoadSaver).Load(pm) |
- }, |
- setKey: func(slot reflect.Value, k *Key) { |
- PopulateKey(slot.Interface(), k) |
- }, |
+ // Generate new element. |
+ // |
+ // If a map/chan type implements an interface, its pointer is also considered |
+ // to implement that interface. |
+ // |
+ // In this case, we have special pointer-to-map/chan logic in multiArgTypePLS. |
+ mat.newElem = func() reflect.Value { |
+ return reflect.New(et).Elem() |
} |
+ |
switch et.Kind() { |
case reflect.Map: |
- ret.newElem = func() reflect.Value { |
+ mat.newElem = func() reflect.Value { |
return reflect.MakeMap(et) |
} |
case reflect.Chan: |
- ret.newElem = func() reflect.Value { |
+ mat.newElem = func() reflect.Value { |
return reflect.MakeChan(et, 0) |
} |
@@ -116,140 +168,45 @@ func multiArgTypePLS(et reflect.Type) *multiArgType { |
elem := et.Elem() |
switch elem.Kind() { |
case reflect.Map: |
- ret.newElem = func() reflect.Value { |
+ mat.newElem = func() reflect.Value { |
ptr := reflect.New(elem) |
ptr.Elem().Set(reflect.MakeMap(elem)) |
return ptr |
} |
case reflect.Chan: |
- ret.newElem = func() reflect.Value { |
+ mat.newElem = func() reflect.Value { |
ptr := reflect.New(elem) |
ptr.Elem().Set(reflect.MakeChan(elem, 0)) |
return ptr |
} |
- } |
- } |
- if ret.newElem == nil { |
- ret.newElem = func() reflect.Value { |
- return reflect.New(et.Elem()) |
+ default: |
+ mat.newElem = func() reflect.Value { return reflect.New(et.Elem()) } |
} |
- } |
- return &ret |
-} |
-// multiArgTypePLSPtr handles the case where et doesn't implement |
-// PropertyLoadSaver, but a pointer to et does. |
-func multiArgTypePLSPtr(et reflect.Type) *multiArgType { |
- return &multiArgType{ |
- getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { |
- return newKeyObjErr(aid, ns, getMGS(slot.Addr().Interface())) |
- }, |
- getPM: func(slot reflect.Value) (PropertyMap, error) { |
- return slot.Addr().Interface().(PropertyLoadSaver).Save(true) |
- }, |
- getMetaPM: func(slot reflect.Value) PropertyMap { |
- return getMGS(slot.Addr().Interface()).GetAllMeta() |
- }, |
- setPM: func(slot reflect.Value, pm PropertyMap) error { |
- return slot.Addr().Interface().(PropertyLoadSaver).Load(pm) |
- }, |
- setKey: func(slot reflect.Value, k *Key) { |
- PopulateKey(slot.Addr().Interface(), k) |
- }, |
- newElem: func() reflect.Value { |
- return reflect.New(et).Elem() |
- }, |
- } |
-} |
- |
-// multiArgTypeStruct == []S |
-func multiArgTypeStruct(et reflect.Type) *multiArgType { |
- cdc := getCodec(et) |
- toPLS := func(slot reflect.Value) *structPLS { |
- return &structPLS{slot, cdc} |
- } |
- return &multiArgType{ |
- getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { |
- return newKeyObjErr(aid, ns, getMGS(slot.Addr().Interface())) |
- }, |
- getPM: func(slot reflect.Value) (PropertyMap, error) { |
- return toPLS(slot).Save(true) |
- }, |
- getMetaPM: func(slot reflect.Value) PropertyMap { |
- return getMGS(slot.Addr().Interface()).GetAllMeta() |
- }, |
- setPM: func(slot reflect.Value, pm PropertyMap) error { |
- return toPLS(slot).Load(pm) |
- }, |
- setKey: func(slot reflect.Value, k *Key) { |
- PopulateKey(toPLS(slot), k) |
- }, |
- newElem: func() reflect.Value { |
- return reflect.New(et).Elem() |
- }, |
+ case reflect.Interface: |
+ mat.newElem = nil |
} |
-} |
-// multiArgTypeStructPtr == []*S |
-func multiArgTypeStructPtr(et reflect.Type) *multiArgType { |
- cdc := getCodec(et.Elem()) |
- toPLS := func(slot reflect.Value) *structPLS { |
- return &structPLS{slot.Elem(), cdc} |
- } |
- return &multiArgType{ |
- getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { |
- return newKeyObjErr(aid, ns, getMGS(slot.Interface())) |
- }, |
- getPM: func(slot reflect.Value) (PropertyMap, error) { |
- return toPLS(slot).Save(true) |
- }, |
- getMetaPM: func(slot reflect.Value) PropertyMap { |
- return getMGS(slot.Interface()).GetAllMeta() |
- }, |
- setPM: func(slot reflect.Value, pm PropertyMap) error { |
- return toPLS(slot).Load(pm) |
- }, |
- setKey: func(slot reflect.Value, k *Key) { |
- PopulateKey(toPLS(slot), k) |
- }, |
- newElem: func() reflect.Value { |
- return reflect.New(et.Elem()) |
- }, |
- } |
+ return &mat |
} |
-// multiArgTypeInterface == []I |
-func multiArgTypeInterface() *multiArgType { |
- return &multiArgType{ |
- getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { |
- return newKeyObjErr(aid, ns, getMGS(slot.Elem().Interface())) |
- }, |
- getPM: func(slot reflect.Value) (PropertyMap, error) { |
- return mkPLS(slot.Elem().Interface()).Save(true) |
- }, |
- getMetaPM: func(slot reflect.Value) PropertyMap { |
- return getMGS(slot.Elem().Interface()).GetAllMeta() |
- }, |
- setPM: func(slot reflect.Value, pm PropertyMap) error { |
- return mkPLS(slot.Elem().Interface()).Load(pm) |
- }, |
- setKey: func(slot reflect.Value, k *Key) { |
- PopulateKey(slot.Elem().Interface(), k) |
- }, |
+// mustParseMultiArg checks that v has type []S, []*S, []I, []P or []*P, for |
+// some struct type S, for some interface type I, or some non-interface |
+// non-pointer type P such that P or *P implements PropertyLoadSaver. |
+func mustParseMultiArg(et reflect.Type) *multiArgType { |
+ if et.Kind() != reflect.Slice { |
+ panic(fmt.Errorf("invalid argument type: expected slice, got %s", et)) |
} |
+ return mustParseArg(et.Elem()) |
} |
-// multiArgTypeKeyExtraction == *Key |
-// |
-// This ONLY implements getKey. |
-func multiArgTypeKeyExtraction() *multiArgType { |
- return &multiArgType{ |
- getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { |
- return slot.Interface().(*Key), nil |
- }, |
+func mustParseArg(et reflect.Type) *multiArgType { |
+ if mat := parseArg(et, false); mat != nil { |
+ return mat |
} |
+ panic(fmt.Errorf("invalid argument type: %s is not a PLS or pointer-to-struct", et)) |
} |
func newKeyObjErr(aid, ns string, mgs MetaGetterSetter) (*Key, error) { |
@@ -273,13 +230,6 @@ func newKeyObjErr(aid, ns string, mgs MetaGetterSetter) (*Key, error) { |
return NewKey(aid, ns, kind, sid, iid, par), nil |
} |
-func mkPLS(o interface{}) PropertyLoadSaver { |
- if pls, ok := o.(PropertyLoadSaver); ok { |
- return pls |
- } |
- return GetPLS(o) |
-} |
- |
func isOKSingleType(t reflect.Type, keysOnly bool) error { |
switch { |
case t == nil: |
@@ -288,15 +238,32 @@ func isOKSingleType(t reflect.Type, keysOnly bool) error { |
return nil |
case !keysOnly && t == typeOfKey: |
return errors.New("not user datatype") |
+ |
case t.Kind() != reflect.Ptr: |
return errors.New("not a pointer") |
case t.Elem().Kind() != reflect.Struct: |
return errors.New("does not point to a struct") |
+ |
default: |
return nil |
} |
} |
+// keyExtractionMGS is a MetaGetterSetter that wraps a key and only implements |
+// GetMeta, and even then only retrieves the wrapped key meta value, "key". |
+type keyExtractionMGS struct { |
+ MetaGetterSetter |
+ |
+ key *Key |
+} |
+ |
+func (mgs *keyExtractionMGS) GetMeta(key string) (interface{}, bool) { |
+ if key == "key" { |
+ return mgs.key, true |
+ } |
+ return nil, false |
+} |
+ |
type metaMultiArgElement struct { |
arg reflect.Value |
mat *multiArgType |