| Index: service/datastore/datastore.go
 | 
| diff --git a/service/datastore/datastore.go b/service/datastore/datastore.go
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..b4f1220b0f1311b5753a4c8b511b811415e1883a
 | 
| --- /dev/null
 | 
| +++ b/service/datastore/datastore.go
 | 
| @@ -0,0 +1,199 @@
 | 
| +// 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) KeyForObj(src interface{}) Key {
 | 
| +	ret, err := d.KeyForObjErr(src)
 | 
| +	if err != nil {
 | 
| +		panic(err)
 | 
| +	}
 | 
| +	return ret
 | 
| +}
 | 
| +
 | 
| +func (d *datastoreImpl) KeyForObjErr(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)
 | 
| +		})
 | 
| +	}
 | 
| +
 | 
| +	mat := parseArg(reflect.TypeOf(proto))
 | 
| +	if !mat.valid || mat.newElem == 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 {
 | 
| +		itm := mat.newElem()
 | 
| +		if innerErr = mat.setPM(itm, pm); innerErr != nil {
 | 
| +			return false
 | 
| +		}
 | 
| +		mat.setKey(itm, k)
 | 
| +		return cb(itm.Interface(), gc)
 | 
| +	})
 | 
| +	if err == nil {
 | 
| +		err = innerErr
 | 
| +	}
 | 
| +	return err
 | 
| +}
 | 
| +
 | 
| +func (d *datastoreImpl) GetAll(q Query, dst interface{}) error {
 | 
| +	v := reflect.ValueOf(dst)
 | 
| +	if v.Kind() != reflect.Ptr {
 | 
| +		return fmt.Errorf("invalid GetAll dst: must have a ptr-to-slice: %T", dst)
 | 
| +	}
 | 
| +	if !v.IsValid() || v.IsNil() {
 | 
| +		return errors.New("invalid GetAll dst: <nil>")
 | 
| +	}
 | 
| +
 | 
| +	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 := parseMultiArg(slice.Type())
 | 
| +	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 {
 | 
| +		slice.Set(reflect.Append(slice, mat.newElem()))
 | 
| +		itm := slice.Index(i)
 | 
| +		mat.setKey(itm, k)
 | 
| +		lme.Assign(i, mat.setPM(itm, pm))
 | 
| +		i++
 | 
| +		return true
 | 
| +	})
 | 
| +	if err == nil {
 | 
| +		err = lme.Get()
 | 
| +	}
 | 
| +	return err
 | 
| +}
 | 
| +
 | 
| +func isOkType(v reflect.Type) bool {
 | 
| +	if v.Implements(typeOfPropertyLoadSaver) {
 | 
| +		return true
 | 
| +	}
 | 
| +	if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
 | 
| +		return true
 | 
| +	}
 | 
| +	return false
 | 
| +}
 | 
| +
 | 
| +func (d *datastoreImpl) Get(dst interface{}) (err error) {
 | 
| +	if !isOkType(reflect.TypeOf(dst)) {
 | 
| +		return fmt.Errorf("invalid Get input type: %T", dst)
 | 
| +	}
 | 
| +	return errors.SingleError(d.GetMulti([]interface{}{dst}))
 | 
| +}
 | 
| +
 | 
| +func (d *datastoreImpl) Put(src interface{}) (err error) {
 | 
| +	if !isOkType(reflect.TypeOf(src)) {
 | 
| +		return fmt.Errorf("invalid Put input type: %T", src)
 | 
| +	}
 | 
| +	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) {
 | 
| +		if key != keys[i] {
 | 
| +			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
 | 
| +}
 | 
| 
 |