Chromium Code Reviews| Index: service/datastore/multiarg.go |
| diff --git a/service/datastore/multiarg.go b/service/datastore/multiarg.go |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..31067d7ae4c159b7b277d7411f3b0286ba5c6b27 |
| --- /dev/null |
| +++ b/service/datastore/multiarg.go |
| @@ -0,0 +1,294 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
|
Vadim Sh.
2015/07/29 16:21:43
I miss templates :)
iannucci
2015/08/03 03:56:32
Yeah me too :)
|
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package datastore |
| + |
| +import ( |
| + "fmt" |
| + "reflect" |
| + |
| + "github.com/luci/luci-go/common/errors" |
| +) |
| + |
| +type multiArgType struct { |
| + valid bool |
| + |
| + getKey func(nk newKeyFunc, slot reflect.Value) (Key, error) |
| + getPM func(slot reflect.Value) (PropertyMap, error) |
| + setPM func(slot reflect.Value, pm PropertyMap) error |
| + setKey func(slot reflect.Value, k Key) |
| + newElem func() reflect.Value |
| +} |
| + |
| +func (mat *multiArgType) GetKeys(nk newKeyFunc, slice reflect.Value) ([]Key, error) { |
| + ret := make([]Key, slice.Len()) |
| + lme := errors.LazyMultiError{Size: len(ret)} |
| + for i := range ret { |
| + key, err := mat.getKey(nk, slice.Index(i)) |
| + lme.Assign(i, err) |
| + ret[i] = key |
| + } |
| + return ret, lme.Get() |
| +} |
| + |
| +func (mat *multiArgType) GetPMs(slice reflect.Value) ([]PropertyMap, error) { |
| + ret := make([]PropertyMap, slice.Len()) |
| + lme := errors.LazyMultiError{Size: len(ret)} |
| + for i := range ret { |
| + key, err := mat.getPM(slice.Index(i)) |
| + lme.Assign(i, err) |
| + ret[i] = key |
| + } |
| + return ret, lme.Get() |
| +} |
| + |
| +// 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 parseMultiArg(e reflect.Type) multiArgType { |
| + if e.Kind() != reflect.Slice { |
| + return multiArgTypeInvalid() |
| + } |
| + return parseArg(e.Elem()) |
| +} |
| + |
| +// parseArg checks that et is of 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 parseArg(et reflect.Type) multiArgType { |
| + if reflect.PtrTo(et).Implements(typeOfPropertyLoadSaver) { |
| + return multiArgTypePLS(et) |
| + } |
| + switch et.Kind() { |
| + case reflect.Struct: |
| + return multiArgTypeStruct(et) |
| + case reflect.Interface: |
| + return multiArgTypeInterface() |
| + case reflect.Ptr: |
| + et = et.Elem() |
| + switch et.Kind() { |
| + case reflect.Ptr: |
| + return multiArgTypePLSPtr(et) |
| + case reflect.Struct: |
| + return multiArgTypeStructPtr(et) |
| + } |
| + } |
| + return multiArgTypeInvalid() |
| +} |
| + |
| +type newKeyFunc func(kind, sid string, iid int64, par Key) Key |
| + |
| +func multiArgTypeInvalid() multiArgType { |
| + return multiArgType{} |
| +} |
| + |
| +// multiArgTypePLS == []P |
| +// *P implements PropertyLoadSaver |
| +func multiArgTypePLS(et reflect.Type) multiArgType { |
| + ret := multiArgType{ |
| + valid: true, |
| + |
| + getKey: func(nk newKeyFunc, slot reflect.Value) (Key, error) { |
| + return newKeyObjErr(nk, slot.Addr().Interface()) |
| + }, |
| + getPM: func(slot reflect.Value) (PropertyMap, error) { |
| + return slot.Addr().Interface().(PropertyLoadSaver).Save(true) |
| + }, |
| + setPM: func(slot reflect.Value, pm PropertyMap) error { |
| + return slot.Addr().Interface().(PropertyLoadSaver).Load(pm) |
| + }, |
| + setKey: func(slot reflect.Value, k Key) { |
| + setKey(slot.Addr().Interface(), k) |
| + }, |
| + } |
| + if et.Kind() == reflect.Map { |
| + ret.newElem = func() reflect.Value { |
| + return reflect.MakeMap(et) |
| + } |
| + } else { |
| + ret.newElem = func() reflect.Value { |
| + return reflect.New(et).Elem() |
| + } |
| + } |
| + return ret |
| +} |
| + |
| +// multiArgTypePLSPtr == []*P |
| +// *P implements PropertyLoadSaver |
| +func multiArgTypePLSPtr(et reflect.Type) multiArgType { |
| + ret := multiArgType{ |
| + valid: true, |
| + |
| + getKey: func(nk newKeyFunc, slot reflect.Value) (Key, error) { |
| + return newKeyObjErr(nk, slot.Interface()) |
| + }, |
| + getPM: func(slot reflect.Value) (PropertyMap, error) { |
| + return slot.Interface().(PropertyLoadSaver).Save(true) |
| + }, |
| + setPM: func(slot reflect.Value, pm PropertyMap) error { |
| + return slot.Interface().(PropertyLoadSaver).Load(pm) |
| + }, |
| + setKey: func(slot reflect.Value, k Key) { |
| + setKey(slot.Interface(), k) |
| + }, |
| + } |
| + e := et.Elem() |
| + if e.Kind() == reflect.Map { |
| + ret.newElem = func() reflect.Value { |
| + ptr := reflect.New(e) |
| + ptr.Elem().Set(reflect.MakeMap(e)) |
| + return ptr |
| + } |
| + } else { |
| + ret.newElem = func() reflect.Value { return reflect.New(e) } |
| + } |
| + return ret |
| +} |
| + |
| +// multiArgTypeStruct == []S |
| +func multiArgTypeStruct(et reflect.Type) multiArgType { |
| + cdc := getCodec(et) |
| + toPLS := func(slot reflect.Value) PropertyLoadSaver { |
| + return &structPLS{slot, cdc} |
| + } |
| + return multiArgType{ |
| + valid: true, |
| + |
| + getKey: func(nk newKeyFunc, slot reflect.Value) (Key, error) { |
| + return newKeyObjErr(nk, toPLS(slot)) |
| + }, |
| + getPM: func(slot reflect.Value) (PropertyMap, error) { |
| + return toPLS(slot).(PropertyLoadSaver).Save(true) |
| + }, |
| + setPM: func(slot reflect.Value, pm PropertyMap) error { |
| + return toPLS(slot).(PropertyLoadSaver).Load(pm) |
| + }, |
| + setKey: func(slot reflect.Value, k Key) { |
| + setKey(toPLS(slot), k) |
| + }, |
| + newElem: func() reflect.Value { |
| + return reflect.New(et).Elem() |
| + }, |
| + } |
| +} |
| + |
| +// multiArgTypeStructPtr == []*S |
| +func multiArgTypeStructPtr(et reflect.Type) multiArgType { |
| + cdc := getCodec(et.Elem()) |
| + toPLS := func(slot reflect.Value) PropertyLoadSaver { |
| + return &structPLS{slot.Elem(), cdc} |
| + } |
| + return multiArgType{ |
| + valid: true, |
| + |
| + getKey: func(nk newKeyFunc, slot reflect.Value) (Key, error) { |
| + return newKeyObjErr(nk, toPLS(slot)) |
| + }, |
| + getPM: func(slot reflect.Value) (PropertyMap, error) { |
| + return toPLS(slot).(PropertyLoadSaver).Save(true) |
| + }, |
| + setPM: func(slot reflect.Value, pm PropertyMap) error { |
| + return toPLS(slot).(PropertyLoadSaver).Load(pm) |
| + }, |
| + setKey: func(slot reflect.Value, k Key) { |
| + setKey(toPLS(slot), k) |
| + }, |
| + newElem: func() reflect.Value { |
| + return reflect.New(et).Elem() |
| + }, |
| + } |
| +} |
| + |
| +// multiArgTypeInterface == []I |
| +func multiArgTypeInterface() multiArgType { |
| + return multiArgType{ |
| + valid: true, |
| + |
| + getKey: func(nk newKeyFunc, slot reflect.Value) (Key, error) { |
| + return newKeyObjErr(nk, slot.Interface()) |
| + }, |
| + getPM: func(slot reflect.Value) (PropertyMap, error) { |
| + pls, _ := mkPLSName(slot.Interface()) |
| + return pls.Save(true) |
| + }, |
| + setPM: func(slot reflect.Value, pm PropertyMap) error { |
| + pls, _ := mkPLSName(slot.Interface()) |
| + return pls.Load(pm) |
| + }, |
| + setKey: func(slot reflect.Value, k Key) { |
| + setKey(slot.Interface(), k) |
| + }, |
| + } |
| +} |
| + |
| +func newKeyObjErr(nk newKeyFunc, src interface{}) (Key, error) { |
| + pls, name := mkPLSName(src) |
| + if key := getMetaKey(pls, "key"); key != nil { |
| + return key, nil |
| + } |
| + |
| + // get kind |
| + kind := getMetaString(pls, "kind", name) |
| + if kind == "" { |
| + return nil, fmt.Errorf("unable to extract $kind from %v", src) |
| + } |
| + |
| + // get id - allow both to be default for default keys |
| + sid := getMetaString(pls, "id", "") |
| + iid := getMetaInt64(pls, "id", 0) |
| + |
| + // get parent |
| + par := getMetaKey(pls, "parent") |
| + |
| + return nk(kind, sid, iid, par), nil |
| +} |
| + |
| +func setKey(src interface{}, key Key) { |
| + pls, _ := mkPLSName(src) |
| + if pls.SetMeta("key", key) == ErrMetaFieldUnset { |
| + if key.StringID() != "" { |
| + pls.SetMeta("id", key.StringID()) |
| + } else { |
| + pls.SetMeta("id", key.IntID()) |
| + } |
| + pls.SetMeta("kind", key.Kind()) |
| + pls.SetMeta("parent", key.Parent()) |
| + } |
| +} |
| + |
| +func mkPLSName(o interface{}) (PropertyLoadSaver, string) { |
| + if pls, ok := o.(*structPLS); ok { |
| + return pls, pls.o.Type().Name() |
| + } |
| + if pls, ok := o.(PropertyLoadSaver); ok { |
| + return pls, "" |
| + } |
| + pls := GetPLS(o) |
| + name := pls.(*structPLS).o.Type().Name() |
| + return pls, name |
| +} |
| + |
| +func getMetaString(pls PropertyLoadSaver, key, dflt string) string { |
| + mstr, err := pls.GetMeta(key) |
| + ret, ok := mstr.(string) |
| + if err != nil || !ok { |
| + return dflt |
| + } |
| + return ret |
| +} |
| + |
| +func getMetaInt64(pls PropertyLoadSaver, key string, dflt int64) int64 { |
| + mint, err := pls.GetMeta(key) |
| + ret, ok := mint.(int64) |
| + if err != nil || !ok { |
| + return dflt |
| + } |
| + return ret |
| +} |
| + |
| +func getMetaKey(pls PropertyLoadSaver, key string) Key { |
| + mkey, _ := pls.GetMeta(key) |
| + ret, _ := mkey.(Key) |
| + return ret |
| +} |