Index: service/datastore/datastore.go |
diff --git a/service/datastore/datastore.go b/service/datastore/datastore.go |
index a6ae3e9e3509e2a9a0ab6bde7e90cb859d358699..3b05f392b8b336ec7e1df474475641be8f881b12 100644 |
--- a/service/datastore/datastore.go |
+++ b/service/datastore/datastore.go |
@@ -44,68 +44,114 @@ func (d *datastoreImpl) NewKeyToks(toks []KeyTok) *Key { |
return NewKeyToks(d.aid, d.ns, toks) |
} |
-func (d *datastoreImpl) Run(q *Query, cbIface interface{}) error { |
+func runParseCallback(cbIface interface{}) (isKey, hasErr, hasCursorCB bool, mat multiArgType) { |
+ badSig := func() { |
+ panic(fmt.Errorf( |
+ "cb does not match the required callback signature: `%T` != `func(TYPE, [CursorCB]) [error]`", |
+ cbIface)) |
+ } |
+ |
if cbIface == nil { |
- return fmt.Errorf("cannot use nil callback when Running query") |
+ badSig() |
} |
// TODO(riannucci): Profile and determine if any of this is causing a real |
// slowdown. Could potentially cache reflection stuff by cbTyp? |
cbTyp := reflect.TypeOf(cbIface) |
- badSig := false |
- mat := multiArgType{} |
- isKey := false |
- |
- if cbTyp.Kind() == reflect.Func && cbTyp.NumIn() == 2 && cbTyp.NumOut() == 1 { |
- firstArg := cbTyp.In(0) |
- if firstArg == typeOfKey { |
- isKey = true |
- } else { |
- mat = parseArg(firstArg, false) |
- badSig = mat.newElem == nil |
- } |
- } else { |
- badSig = true |
+ if cbTyp.Kind() != reflect.Func { |
+ badSig() |
} |
- if badSig || cbTyp.Out(0) != typeOfBool || cbTyp.In(1) != typeOfCursorCB { |
- panic(fmt.Errorf( |
- "cb does not match the required callback signature: `%T` != `func(TYPE, CursorCB) bool`", |
- cbIface)) |
+ numIn := cbTyp.NumIn() |
+ if numIn != 1 && numIn != 2 { |
+ badSig() |
} |
- if isKey { |
- cb := cbIface.(func(*Key, CursorCB) bool) |
- fq, err := q.KeysOnly(true).Finalize() |
- if err != nil { |
- return err |
+ firstArg := cbTyp.In(0) |
+ if firstArg == typeOfKey { |
+ isKey = true |
+ } else { |
+ mat = parseArg(firstArg, false) |
+ if mat.newElem == nil { |
+ badSig() |
} |
- return d.RawInterface.Run(fq, func(k *Key, _ PropertyMap, gc CursorCB) bool { |
- return cb(k, gc) |
- }) |
} |
+ hasCursorCB = numIn == 2 |
+ if hasCursorCB && cbTyp.In(1) != typeOfCursorCB { |
+ badSig() |
+ } |
+ |
+ if cbTyp.NumOut() > 1 { |
+ badSig() |
+ } else if cbTyp.NumOut() == 1 && cbTyp.Out(0) != typeOfError { |
+ badSig() |
+ } |
+ hasErr = cbTyp.NumOut() == 1 |
+ |
+ return |
+} |
+ |
+func (d *datastoreImpl) Run(q *Query, cbIface interface{}) error { |
+ isKey, hasErr, hasCursorCB, mat := runParseCallback(cbIface) |
+ |
+ if isKey { |
+ q = q.KeysOnly(true) |
+ } |
fq, err := q.Finalize() |
if err != nil { |
return err |
} |
cbVal := reflect.ValueOf(cbIface) |
+ var cb func(reflect.Value, CursorCB) error |
+ switch { |
+ case hasErr && hasCursorCB: |
+ cb = func(v reflect.Value, cb CursorCB) error { |
+ err := cbVal.Call([]reflect.Value{v, reflect.ValueOf(cb)})[0].Interface() |
+ if err != nil { |
+ return err.(error) |
+ } |
+ return nil |
+ } |
+ |
+ case hasErr && !hasCursorCB: |
+ cb = func(v reflect.Value, _ CursorCB) error { |
+ err := cbVal.Call([]reflect.Value{v})[0].Interface() |
+ if err != nil { |
+ return err.(error) |
+ } |
+ return nil |
+ } |
+ |
+ case !hasErr && hasCursorCB: |
+ cb = func(v reflect.Value, cb CursorCB) error { |
+ cbVal.Call([]reflect.Value{v, reflect.ValueOf(cb)}) |
+ return nil |
+ } |
+ |
+ case !hasErr && !hasCursorCB: |
+ cb = func(v reflect.Value, _ CursorCB) error { |
+ cbVal.Call([]reflect.Value{v}) |
+ return nil |
+ } |
+ } |
+ |
+ if isKey { |
+ return d.RawInterface.Run(fq, func(k *Key, _ PropertyMap, gc CursorCB) error { |
+ return cb(reflect.ValueOf(k), gc) |
+ }) |
+ } |
- innerErr := error(nil) |
- err = d.RawInterface.Run(fq, func(k *Key, pm PropertyMap, gc CursorCB) bool { |
+ return d.RawInterface.Run(fq, func(k *Key, pm PropertyMap, gc CursorCB) error { |
itm := mat.newElem() |
- if innerErr = mat.setPM(itm, pm); innerErr != nil { |
- return false |
+ if err := mat.setPM(itm, pm); err != nil { |
+ return err |
} |
mat.setKey(itm, k) |
- return cbVal.Call([]reflect.Value{itm, reflect.ValueOf(gc)})[0].Bool() |
+ return cb(itm, gc) |
}) |
- if err == nil { |
- err = innerErr |
- } |
- return err |
} |
func (d *datastoreImpl) Count(q *Query) (int64, error) { |
@@ -131,9 +177,9 @@ func (d *datastoreImpl) GetAll(q *Query, dst interface{}) error { |
return err |
} |
- return d.RawInterface.Run(fq, func(k *Key, _ PropertyMap, _ CursorCB) bool { |
+ return d.RawInterface.Run(fq, func(k *Key, _ PropertyMap, _ CursorCB) error { |
*keys = append(*keys, k) |
- return true |
+ return nil |
}) |
} |
fq, err := q.Finalize() |
@@ -149,7 +195,7 @@ func (d *datastoreImpl) GetAll(q *Query, dst interface{}) error { |
errs := map[int]error{} |
i := 0 |
- err = d.RawInterface.Run(fq, func(k *Key, pm PropertyMap, _ CursorCB) bool { |
+ err = d.RawInterface.Run(fq, func(k *Key, pm PropertyMap, _ CursorCB) error { |
slice.Set(reflect.Append(slice, mat.newElem())) |
itm := slice.Index(i) |
mat.setKey(itm, k) |
@@ -158,7 +204,7 @@ func (d *datastoreImpl) GetAll(q *Query, dst interface{}) error { |
errs[i] = err |
} |
i++ |
- return true |
+ return nil |
}) |
if err == nil { |
if len(errs) > 0 { |
@@ -186,13 +232,14 @@ func (d *datastoreImpl) ExistsMulti(keys []*Key) ([]bool, error) { |
lme := errors.NewLazyMultiError(len(keys)) |
ret := make([]bool, len(keys)) |
i := 0 |
- err := d.RawInterface.GetMulti(keys, nil, func(_ PropertyMap, err error) { |
+ err := d.RawInterface.GetMulti(keys, nil, func(_ PropertyMap, err error) error { |
if err == nil { |
ret[i] = true |
} else if err != ErrNoSuchEntity { |
lme.Assign(i, err) |
} |
i++ |
+ return nil |
}) |
if err != nil { |
return ret, err |
@@ -235,11 +282,12 @@ func (d *datastoreImpl) GetMulti(dst interface{}) error { |
lme := errors.NewLazyMultiError(len(keys)) |
i := 0 |
meta := NewMultiMetaGetter(pms) |
- err = d.RawInterface.GetMulti(keys, meta, func(pm PropertyMap, err error) { |
+ err = d.RawInterface.GetMulti(keys, meta, func(pm PropertyMap, err error) error { |
if !lme.Assign(i, err) { |
lme.Assign(i, mat.setPM(slice.Index(i), pm)) |
} |
i++ |
+ return nil |
}) |
if err == nil { |
@@ -259,12 +307,13 @@ func (d *datastoreImpl) PutMulti(src interface{}) error { |
lme := errors.NewLazyMultiError(len(keys)) |
i := 0 |
- err = d.RawInterface.PutMulti(keys, vals, func(key *Key, err error) { |
+ err = d.RawInterface.PutMulti(keys, vals, func(key *Key, err error) error { |
if key != keys[i] { |
mat.setKey(slice.Index(i), key) |
} |
lme.Assign(i, err) |
i++ |
+ return nil |
}) |
if err == nil { |
@@ -276,9 +325,10 @@ func (d *datastoreImpl) PutMulti(src interface{}) error { |
func (d *datastoreImpl) DeleteMulti(keys []*Key) (err error) { |
lme := errors.NewLazyMultiError(len(keys)) |
i := 0 |
- extErr := d.RawInterface.DeleteMulti(keys, func(internalErr error) { |
+ extErr := d.RawInterface.DeleteMulti(keys, func(internalErr error) error { |
lme.Assign(i, internalErr) |
i++ |
+ return nil |
}) |
err = lme.Get() |
if err == nil { |