Chromium Code Reviews| Index: service/datastore/datastore.go |
| diff --git a/service/datastore/datastore.go b/service/datastore/datastore.go |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b2169038ac9e85dcea5248d716cc7e593855eb9d |
| --- /dev/null |
| +++ b/service/datastore/datastore.go |
| @@ -0,0 +1,200 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// 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 datastoreImpl struct{ RawInterface } |
| + |
| +var _ Interface = (*datastoreImpl)(nil) |
| + |
| +func (d *datastoreImpl) NewKeyObj(src interface{}) Key { |
| + ret, err := d.NewKeyObjErr(src) |
| + if err != nil { |
| + panic(err) |
| + } |
| + return ret |
| +} |
| + |
| +func (d *datastoreImpl) NewKeyObjErr(src interface{}) (Key, error) { |
| + return newKeyObjErr(d.NewKey, src) |
| +} |
| + |
| +func (d *datastoreImpl) Run(q Query, proto interface{}, cb RunCB) error { |
| + if _, ok := proto.(*Key); ok { |
| + return d.RawInterface.Run(q.KeysOnly(), func(k Key, _ PropertyMap, gc func() (Cursor, error)) bool { |
| + return cb(k, gc) |
| + }) |
| + } |
| + |
| + t := reflect.TypeOf(proto) |
| + if t.Kind() != reflect.Ptr { |
| + return fmt.Errorf("invalid Run proto type: %T", proto) |
| + } |
| + e := t.Elem() |
| + var mkPLS func() (interface{}, PropertyLoadSaver) |
| + if t.Implements(typeOfPropertyLoadSaver) { |
| + mkPLS = func() (interface{}, PropertyLoadSaver) { |
| + iface := reflect.New(e).Interface() |
| + return iface, iface.(PropertyLoadSaver) |
| + } |
| + } else if e.Kind() == reflect.Struct { |
| + cdc := getCodec(e) |
| + mkPLS = func() (interface{}, PropertyLoadSaver) { |
| + ptr := reflect.New(e) |
| + return ptr.Interface(), &structPLS{ptr.Elem(), cdc} |
| + } |
| + } |
| + if mkPLS == nil { |
| + return fmt.Errorf("invalid Run proto type: %T", proto) |
| + } |
| + |
| + innerErr := error(nil) |
| + err := d.RawInterface.Run(q, func(k Key, pm PropertyMap, gc func() (Cursor, error)) bool { |
| + iface, pls := mkPLS() |
| + if innerErr = pls.Load(pm); innerErr != nil { |
| + return false |
| + } |
| + setKey(pls, k) |
| + return cb(iface, gc) |
| + }) |
| + if err == nil { |
| + err = innerErr |
|
Vadim Sh.
2015/07/29 16:21:43
are you sure you want to return error(nil) instead
iannucci
2015/08/03 03:56:31
pretty sure it's the same thing. `innerErr := err
|
| + } |
| + return err |
| +} |
| + |
| +func (d *datastoreImpl) GetAll(q Query, dst interface{}) error { |
| + v := reflect.ValueOf(dst) |
| + if v.IsNil() { |
| + return errors.New("invalid GetAll dst: <nil>") |
| + } |
| + |
| + t := v.Type() |
| + if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice { |
| + return fmt.Errorf("invalid GetAll dst: must have a ptr-to-slice %T", dst) |
| + } |
| + |
| + if keys, ok := dst.(*[]Key); ok { |
| + return d.RawInterface.Run(q.KeysOnly(), func(k Key, _ PropertyMap, _ func() (Cursor, error)) bool { |
| + *keys = append(*keys, k) |
| + return true |
| + }) |
| + } |
| + |
| + slice := v.Elem() |
| + mat := parseArg(t.Elem()) |
| + if !mat.valid || mat.newElem == nil { |
| + return fmt.Errorf("invalid GetAll input type: %T", dst) |
| + } |
| + |
| + lme := errors.LazyMultiError{Size: slice.Len()} |
| + i := 0 |
| + err := d.RawInterface.Run(q, func(k Key, pm PropertyMap, _ func() (Cursor, error)) bool { |
| + itm := mat.newElem() |
| + slice.Set(reflect.Append(slice, itm)) |
| + mat.setKey(itm, k) |
| + lme.Assign(i, mat.setPM(itm, pm)) |
| + i++ |
| + return true |
| + }) |
| + if err == nil { |
| + err = lme.Get() |
| + } |
| + return err |
| +} |
| + |
| +func (d *datastoreImpl) Get(dst interface{}) (err error) { |
| + return errors.SingleError(d.GetMulti([]interface{}{dst})) |
| +} |
| + |
| +func (d *datastoreImpl) Put(src interface{}) (err error) { |
| + return errors.SingleError(d.PutMulti([]interface{}{src})) |
| +} |
| + |
| +func (d *datastoreImpl) Delete(key Key) (err error) { |
| + return errors.SingleError(d.DeleteMulti([]Key{key})) |
| +} |
| + |
| +func (d *datastoreImpl) GetMulti(dst interface{}) error { |
| + slice := reflect.ValueOf(dst) |
| + mat := parseMultiArg(slice.Type()) |
| + if !mat.valid { |
| + return fmt.Errorf("invalid GetMulti input type: %T", dst) |
| + } |
| + |
| + keys, err := mat.GetKeys(d.NewKey, slice) |
| + if err != nil { |
| + return err |
| + } |
| + |
| + lme := errors.LazyMultiError{Size: len(keys)} |
| + i := 0 |
| + err = d.RawInterface.GetMulti(keys, func(pm PropertyMap, err error) { |
| + if !lme.Assign(i, err) { |
| + lme.Assign(i, mat.setPM(slice.Index(i), pm)) |
| + } |
| + i++ |
| + }) |
| + |
| + if err == nil { |
| + err = lme.Get() |
| + } |
| + return err |
| +} |
| + |
| +func (d *datastoreImpl) PutMulti(src interface{}) error { |
| + slice := reflect.ValueOf(src) |
| + mat := parseMultiArg(slice.Type()) |
| + if !mat.valid { |
| + return fmt.Errorf("invalid PutMulti input type: %T", src) |
| + } |
| + |
| + keys, err := mat.GetKeys(d.NewKey, slice) |
| + if err != nil { |
| + return err |
| + } |
| + |
| + vals, err := mat.GetPMs(slice) |
| + if err != nil { |
| + return err |
| + } |
| + |
| + lme := errors.LazyMultiError{Size: len(keys)} |
| + i := 0 |
| + err = d.RawInterface.PutMulti(keys, vals, func(key Key, err error) { |
| + mat.setKey(slice.Index(i), key) |
| + lme.Assign(i, err) |
| + i++ |
| + }) |
| + |
| + if err == nil { |
| + err = lme.Get() |
| + } |
| + return err |
| +} |
| + |
| +func (d *datastoreImpl) DeleteMulti(keys []Key) (err error) { |
| + lme := errors.LazyMultiError{Size: len(keys)} |
| + i := 0 |
| + extErr := d.RawInterface.DeleteMulti(keys, func(internalErr error) { |
| + lme.Assign(i, internalErr) |
| + i++ |
| + }) |
| + err = lme.Get() |
| + if err == nil { |
| + err = extErr |
| + } |
| + return |
| +} |
| + |
| +func (d *datastoreImpl) Raw() RawInterface { |
| + return d.RawInterface |
| +} |