Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package datastore | 5 package datastore |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "fmt" | 8 "fmt" |
| 9 "reflect" | 9 "reflect" |
| 10 | 10 |
| 11 "github.com/luci/luci-go/common/errors" | 11 "github.com/luci/luci-go/common/errors" |
| 12 ) | 12 ) |
| 13 | 13 |
| 14 type multiArgType struct { | 14 type multiArgType struct { |
| 15 getKey func(aid, ns string, slot reflect.Value) (*Key, error) | 15 getKey func(aid, ns string, slot reflect.Value) (*Key, error) |
| 16 getPM func(slot reflect.Value) (PropertyMap, error) | 16 getPM func(slot reflect.Value) (PropertyMap, error) |
| 17 getMetaPM func(slot reflect.Value) PropertyMap | 17 getMetaPM func(slot reflect.Value) PropertyMap |
| 18 setPM func(slot reflect.Value, pm PropertyMap) error | 18 setPM func(slot reflect.Value, pm PropertyMap) error |
| 19 setKey func(slot reflect.Value, k *Key) | 19 setKey func(slot reflect.Value, k *Key) |
| 20 newElem func() reflect.Value | 20 newElem func() reflect.Value |
| 21 } | 21 } |
| 22 | 22 |
| 23 func (mat *multiArgType) GetKeysPMs(aid, ns string, slice reflect.Value, meta bo ol) ([]*Key, []PropertyMap, error) { | 23 // parseArg checks that et is of type S, *S, I, P or *P, for some |
| 24 » retKey := make([]*Key, slice.Len()) | 24 // struct type S, for some interface type I, or some non-interface non-pointer |
| 25 » retPM := make([]PropertyMap, slice.Len()) | 25 // type P such that P or *P implements PropertyLoadSaver. |
| 26 » getter := mat.getPM | 26 // |
| 27 » if meta { | 27 // If allowKeys is true, a read-only key extraction multiArgType will be |
| 28 » » getter = func(slot reflect.Value) (PropertyMap, error) { | 28 // returned if et is a *Key. |
| 29 » » » return mat.getMetaPM(slot), nil | 29 func parseArg(et reflect.Type, keysOnly bool) *multiArgType { |
|
iannucci
2016/05/28 02:50:53
s/keysOnly/allowKeys
dnj
2016/05/28 17:47:21
Done.
| |
| 30 » if keysOnly && et == typeOfKey { | |
| 31 » » return multiArgTypeKeyExtraction() | |
| 32 » } | |
| 33 | |
| 34 » if et.Kind() == reflect.Interface { | |
| 35 » » return multiArgTypeInterface() | |
| 36 » } | |
| 37 | |
| 38 » // If a map type implements an interface, its pointer is also considered to | |
| 39 » // implement that interface. | |
| 40 » // | |
| 41 » // In this case, we have special pointer-to-map logic in multiArgTypePLS . | |
| 42 » if et.Implements(typeOfPropertyLoadSaver) { | |
| 43 » » return multiArgTypePLS(et) | |
| 44 » } | |
| 45 » if reflect.PtrTo(et).Implements(typeOfPropertyLoadSaver) { | |
| 46 » » return multiArgTypePLSPtr(et) | |
| 47 » } | |
| 48 | |
| 49 » switch et.Kind() { | |
| 50 » case reflect.Ptr: | |
| 51 » » if et.Elem().Kind() == reflect.Struct { | |
| 52 » » » return multiArgTypeStructPtr(et) | |
| 30 } | 53 } |
| 54 | |
| 55 case reflect.Struct: | |
| 56 return multiArgTypeStruct(et) | |
| 31 } | 57 } |
| 32 » lme := errors.NewLazyMultiError(len(retKey)) | 58 |
| 33 » for i := range retKey { | 59 » return nil |
| 34 » » key, err := mat.getKey(aid, ns, slice.Index(i)) | |
| 35 » » if !lme.Assign(i, err) { | |
| 36 » » » retKey[i] = key | |
| 37 » » » pm, err := getter(slice.Index(i)) | |
| 38 » » » if !lme.Assign(i, err) { | |
| 39 » » » » retPM[i] = pm | |
| 40 » » » } | |
| 41 » » } | |
| 42 » } | |
| 43 » return retKey, retPM, lme.Get() | |
| 44 } | 60 } |
| 45 | 61 |
| 46 // parseMultiArg checks that v has type []S, []*S, []I, []P or []*P, for some | 62 // parseMultiArg checks that v has type []S, []*S, []I, []P or []*P, for some |
| 47 // struct type S, for some interface type I, or some non-interface non-pointer | 63 // struct type S, for some interface type I, or some non-interface non-pointer |
| 48 // type P such that P or *P implements PropertyLoadSaver. | 64 // type P such that P or *P implements PropertyLoadSaver. |
| 49 func parseMultiArg(e reflect.Type) multiArgType { | 65 func mustParseMultiArg(et reflect.Type) *multiArgType { |
| 50 » if e.Kind() != reflect.Slice { | 66 » if et.Kind() != reflect.Slice { |
| 51 » » panic(fmt.Errorf("invalid argument type: expected slice, got %s" , e)) | 67 » » panic(fmt.Errorf("invalid argument type: expected slice, got %s" , et)) |
| 52 } | 68 } |
| 53 » return parseArg(e.Elem(), true) | 69 » return mustParseArg(et.Elem()) |
| 54 } | 70 } |
| 55 | 71 |
| 56 // parseArg checks that et is of type S, *S, I, P or *P, for some | 72 func mustParseArg(et reflect.Type) *multiArgType { |
| 57 // struct type S, for some interface type I, or some non-interface non-pointer | 73 » if mat := parseArg(et, false); mat != nil { |
| 58 // type P such that P or *P implements PropertyLoadSaver. | 74 » » return mat |
| 59 func parseArg(et reflect.Type, multi bool) multiArgType { | |
| 60 » if reflect.PtrTo(et).Implements(typeOfPropertyLoadSaver) { | |
| 61 » » return multiArgTypePLS(et) | |
| 62 } | 75 } |
| 63 » if et.Implements(typeOfPropertyLoadSaver) && et.Kind() != reflect.Interf ace { | 76 » panic(fmt.Errorf("invalid argument type: %s is not a PLS or pointer-to-s truct", et)) |
| 64 » » return multiArgTypePLSPtr(et.Elem()) | |
| 65 » } | |
| 66 » switch et.Kind() { | |
| 67 » case reflect.Struct: | |
| 68 » » return multiArgTypeStruct(et) | |
| 69 » case reflect.Interface: | |
| 70 » » return multiArgTypeInterface() | |
| 71 » case reflect.Ptr: | |
| 72 » » et = et.Elem() | |
| 73 » » if et.Kind() == reflect.Struct { | |
| 74 » » » return multiArgTypeStructPtr(et) | |
| 75 » » } | |
| 76 » } | |
| 77 » if multi { | |
| 78 » » panic(fmt.Errorf("invalid argument type: []%s", et)) | |
| 79 » } | |
| 80 » panic(fmt.Errorf("invalid argument type: %s", et)) | |
| 81 } | 77 } |
| 82 | 78 |
| 83 type newKeyFunc func(kind, sid string, iid int64, par Key) Key | 79 // multiArgTypePLS handles the case where et implements PropertyLoadSaver. |
| 84 | 80 // |
| 85 // multiArgTypePLS == []P | 81 // This handles the special case of pointer-to-map (see parseArg). |
| 86 // *P implements PropertyLoadSaver | 82 func multiArgTypePLS(et reflect.Type) *multiArgType { |
| 87 func multiArgTypePLS(et reflect.Type) multiArgType { | |
| 88 ret := multiArgType{ | 83 ret := multiArgType{ |
| 89 getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { | 84 getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { |
| 90 » » » return newKeyObjErr(aid, ns, slot.Addr().Interface()) | 85 » » » return newKeyObjErr(aid, ns, getMGS(slot.Interface())) |
| 91 » » }, | |
| 92 » » getPM: func(slot reflect.Value) (PropertyMap, error) { | |
| 93 » » » return slot.Addr().Interface().(PropertyLoadSaver).Save( true) | |
| 94 » » }, | |
| 95 » » getMetaPM: func(slot reflect.Value) PropertyMap { | |
| 96 » » » return getMGS(slot.Addr().Interface()).GetAllMeta() | |
| 97 » » }, | |
| 98 » » setPM: func(slot reflect.Value, pm PropertyMap) error { | |
| 99 » » » return slot.Addr().Interface().(PropertyLoadSaver).Load( pm) | |
| 100 » » }, | |
| 101 » » setKey: func(slot reflect.Value, k *Key) { | |
| 102 » » » PopulateKey(slot.Addr().Interface(), k) | |
| 103 » » }, | |
| 104 » } | |
| 105 » if et.Kind() == reflect.Map { | |
| 106 » » ret.newElem = func() reflect.Value { | |
| 107 » » » // Create a *map so that way slot.Addr() works above whe n this is | |
| 108 » » » // called from Run(). Otherwise the map is 'unaddressabl e' according | |
| 109 » » » // to reflect. ¯\_(ツ)_/¯ | |
| 110 » » » ptr := reflect.New(et) | |
| 111 » » » ptr.Elem().Set(reflect.MakeMap(et)) | |
| 112 » » » return ptr.Elem() | |
| 113 » » } | |
| 114 » } else { | |
| 115 » » ret.newElem = func() reflect.Value { | |
| 116 » » » return reflect.New(et).Elem() | |
| 117 » » } | |
| 118 » } | |
| 119 » return ret | |
| 120 } | |
| 121 | |
| 122 // multiArgTypePLSPtr == []*P | |
| 123 // *P implements PropertyLoadSaver | |
| 124 func multiArgTypePLSPtr(et reflect.Type) multiArgType { | |
| 125 » ret := multiArgType{ | |
| 126 » » getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { | |
| 127 » » » return newKeyObjErr(aid, ns, slot.Interface()) | |
| 128 }, | 86 }, |
| 129 getPM: func(slot reflect.Value) (PropertyMap, error) { | 87 getPM: func(slot reflect.Value) (PropertyMap, error) { |
| 130 return slot.Interface().(PropertyLoadSaver).Save(true) | 88 return slot.Interface().(PropertyLoadSaver).Save(true) |
| 131 }, | 89 }, |
| 132 getMetaPM: func(slot reflect.Value) PropertyMap { | 90 getMetaPM: func(slot reflect.Value) PropertyMap { |
| 133 return getMGS(slot.Interface()).GetAllMeta() | 91 return getMGS(slot.Interface()).GetAllMeta() |
| 134 }, | 92 }, |
| 135 setPM: func(slot reflect.Value, pm PropertyMap) error { | 93 setPM: func(slot reflect.Value, pm PropertyMap) error { |
| 136 return slot.Interface().(PropertyLoadSaver).Load(pm) | 94 return slot.Interface().(PropertyLoadSaver).Load(pm) |
| 137 }, | 95 }, |
| 138 setKey: func(slot reflect.Value, k *Key) { | 96 setKey: func(slot reflect.Value, k *Key) { |
| 139 PopulateKey(slot.Interface(), k) | 97 PopulateKey(slot.Interface(), k) |
| 140 }, | 98 }, |
| 141 } | 99 } |
| 142 » if et.Kind() == reflect.Map { | 100 » switch et.Kind() { |
| 101 » case reflect.Map: | |
|
iannucci
2016/05/28 02:50:53
I think chans also can hit this.
dnj
2016/05/28 17:47:21
Done. Unfortunately, there is *no way* to communic
| |
| 143 ret.newElem = func() reflect.Value { | 102 ret.newElem = func() reflect.Value { |
| 144 » » » ptr := reflect.New(et) | 103 » » » return reflect.MakeMap(et) |
| 145 » » » ptr.Elem().Set(reflect.MakeMap(et)) | |
| 146 » » » return ptr | |
| 147 } | 104 } |
| 148 » } else { | 105 |
| 149 » » ret.newElem = func() reflect.Value { return reflect.New(et) } | 106 » case reflect.Ptr: |
| 107 » » mapElem := et.Elem() | |
| 108 » » if mapElem.Kind() == reflect.Map { | |
| 109 » » » ret.newElem = func() reflect.Value { | |
| 110 » » » » ptr := reflect.New(mapElem) | |
| 111 » » » » ptr.Elem().Set(reflect.MakeMap(mapElem)) | |
| 112 » » » » return ptr | |
| 113 » » » } | |
| 114 » » } | |
| 150 } | 115 } |
| 151 » return ret | 116 |
| 117 » if ret.newElem == nil { | |
| 118 » » ret.newElem = func() reflect.Value { | |
| 119 » » » return reflect.New(et.Elem()) | |
| 120 » » } | |
| 121 » } | |
| 122 » return &ret | |
| 123 } | |
| 124 | |
| 125 // multiArgTypePLSPtr handles the case where et doesn't implement | |
| 126 // PropertyLoadSaver, but a pointer to et does. | |
| 127 func multiArgTypePLSPtr(et reflect.Type) *multiArgType { | |
| 128 » return &multiArgType{ | |
| 129 » » getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { | |
| 130 » » » return newKeyObjErr(aid, ns, getMGS(slot.Addr().Interfac e())) | |
| 131 » » }, | |
| 132 » » getPM: func(slot reflect.Value) (PropertyMap, error) { | |
| 133 » » » return slot.Addr().Interface().(PropertyLoadSaver).Save( true) | |
| 134 » » }, | |
| 135 » » getMetaPM: func(slot reflect.Value) PropertyMap { | |
| 136 » » » return getMGS(slot.Addr().Interface()).GetAllMeta() | |
| 137 » » }, | |
| 138 » » setPM: func(slot reflect.Value, pm PropertyMap) error { | |
| 139 » » » return slot.Addr().Interface().(PropertyLoadSaver).Load( pm) | |
| 140 » » }, | |
| 141 » » setKey: func(slot reflect.Value, k *Key) { | |
| 142 » » » PopulateKey(slot.Addr().Interface(), k) | |
| 143 » » }, | |
| 144 » » newElem: func() reflect.Value { | |
| 145 » » » return reflect.New(et).Elem() | |
| 146 » » }, | |
| 147 » } | |
| 152 } | 148 } |
| 153 | 149 |
| 154 // multiArgTypeStruct == []S | 150 // multiArgTypeStruct == []S |
| 155 func multiArgTypeStruct(et reflect.Type) multiArgType { | 151 func multiArgTypeStruct(et reflect.Type) *multiArgType { |
| 156 cdc := getCodec(et) | 152 cdc := getCodec(et) |
| 157 toPLS := func(slot reflect.Value) *structPLS { | 153 toPLS := func(slot reflect.Value) *structPLS { |
| 158 return &structPLS{slot, cdc} | 154 return &structPLS{slot, cdc} |
| 159 } | 155 } |
| 160 » return multiArgType{ | 156 » return &multiArgType{ |
| 161 getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { | 157 getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { |
| 162 » » » return newKeyObjErr(aid, ns, toPLS(slot)) | 158 » » » return newKeyObjErr(aid, ns, getMGS(slot.Addr().Interfac e())) |
| 163 }, | 159 }, |
| 164 getPM: func(slot reflect.Value) (PropertyMap, error) { | 160 getPM: func(slot reflect.Value) (PropertyMap, error) { |
| 165 return toPLS(slot).Save(true) | 161 return toPLS(slot).Save(true) |
| 166 }, | 162 }, |
| 167 getMetaPM: func(slot reflect.Value) PropertyMap { | 163 getMetaPM: func(slot reflect.Value) PropertyMap { |
| 168 » » » if slot.Type().Implements(typeOfMGS) { | 164 » » » return getMGS(slot.Addr().Interface()).GetAllMeta() |
| 169 » » » » return slot.Interface().(MetaGetterSetter).GetAl lMeta() | |
| 170 » » » } | |
| 171 » » » return toPLS(slot).GetAllMeta() | |
| 172 }, | 165 }, |
| 173 setPM: func(slot reflect.Value, pm PropertyMap) error { | 166 setPM: func(slot reflect.Value, pm PropertyMap) error { |
| 174 return toPLS(slot).Load(pm) | 167 return toPLS(slot).Load(pm) |
| 175 }, | 168 }, |
| 176 setKey: func(slot reflect.Value, k *Key) { | 169 setKey: func(slot reflect.Value, k *Key) { |
| 177 PopulateKey(toPLS(slot), k) | 170 PopulateKey(toPLS(slot), k) |
| 178 }, | 171 }, |
| 179 newElem: func() reflect.Value { | 172 newElem: func() reflect.Value { |
| 180 return reflect.New(et).Elem() | 173 return reflect.New(et).Elem() |
| 181 }, | 174 }, |
| 182 } | 175 } |
| 183 } | 176 } |
| 184 | 177 |
| 185 // multiArgTypeStructPtr == []*S | 178 // multiArgTypeStructPtr == []*S |
| 186 func multiArgTypeStructPtr(et reflect.Type) multiArgType { | 179 func multiArgTypeStructPtr(et reflect.Type) *multiArgType { |
| 187 » cdc := getCodec(et) | 180 » cdc := getCodec(et.Elem()) |
| 188 toPLS := func(slot reflect.Value) *structPLS { | 181 toPLS := func(slot reflect.Value) *structPLS { |
| 189 return &structPLS{slot.Elem(), cdc} | 182 return &structPLS{slot.Elem(), cdc} |
| 190 } | 183 } |
| 191 » return multiArgType{ | 184 » return &multiArgType{ |
| 192 getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { | 185 getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { |
| 193 » » » return newKeyObjErr(aid, ns, toPLS(slot)) | 186 » » » return newKeyObjErr(aid, ns, getMGS(slot.Interface())) |
| 194 }, | 187 }, |
| 195 getPM: func(slot reflect.Value) (PropertyMap, error) { | 188 getPM: func(slot reflect.Value) (PropertyMap, error) { |
| 196 return toPLS(slot).Save(true) | 189 return toPLS(slot).Save(true) |
| 197 }, | 190 }, |
| 198 getMetaPM: func(slot reflect.Value) PropertyMap { | 191 getMetaPM: func(slot reflect.Value) PropertyMap { |
| 199 » » » if slot.Elem().Type().Implements(typeOfMGS) { | 192 » » » return getMGS(slot.Interface()).GetAllMeta() |
| 200 » » » » return getMGS(slot.Interface()).GetAllMeta() | |
| 201 » » » } | |
| 202 » » » return toPLS(slot).GetAllMeta() | |
| 203 }, | 193 }, |
| 204 setPM: func(slot reflect.Value, pm PropertyMap) error { | 194 setPM: func(slot reflect.Value, pm PropertyMap) error { |
| 205 return toPLS(slot).Load(pm) | 195 return toPLS(slot).Load(pm) |
| 206 }, | 196 }, |
| 207 setKey: func(slot reflect.Value, k *Key) { | 197 setKey: func(slot reflect.Value, k *Key) { |
| 208 PopulateKey(toPLS(slot), k) | 198 PopulateKey(toPLS(slot), k) |
| 209 }, | 199 }, |
| 210 newElem: func() reflect.Value { | 200 newElem: func() reflect.Value { |
| 211 » » » return reflect.New(et) | 201 » » » return reflect.New(et.Elem()) |
| 212 }, | 202 }, |
| 213 } | 203 } |
| 214 } | 204 } |
| 215 | 205 |
| 216 // multiArgTypeInterface == []I | 206 // multiArgTypeInterface == []I |
| 217 func multiArgTypeInterface() multiArgType { | 207 func multiArgTypeInterface() *multiArgType { |
| 218 » return multiArgType{ | 208 » return &multiArgType{ |
| 219 getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { | 209 getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { |
| 220 » » » return newKeyObjErr(aid, ns, slot.Elem().Interface()) | 210 » » » return newKeyObjErr(aid, ns, getMGS(slot.Elem().Interfac e())) |
| 221 }, | 211 }, |
| 222 getPM: func(slot reflect.Value) (PropertyMap, error) { | 212 getPM: func(slot reflect.Value) (PropertyMap, error) { |
| 223 return mkPLS(slot.Elem().Interface()).Save(true) | 213 return mkPLS(slot.Elem().Interface()).Save(true) |
| 224 }, | 214 }, |
| 225 getMetaPM: func(slot reflect.Value) PropertyMap { | 215 getMetaPM: func(slot reflect.Value) PropertyMap { |
| 226 return getMGS(slot.Elem().Interface()).GetAllMeta() | 216 return getMGS(slot.Elem().Interface()).GetAllMeta() |
| 227 }, | 217 }, |
| 228 setPM: func(slot reflect.Value, pm PropertyMap) error { | 218 setPM: func(slot reflect.Value, pm PropertyMap) error { |
| 229 return mkPLS(slot.Elem().Interface()).Load(pm) | 219 return mkPLS(slot.Elem().Interface()).Load(pm) |
| 230 }, | 220 }, |
| 231 setKey: func(slot reflect.Value, k *Key) { | 221 setKey: func(slot reflect.Value, k *Key) { |
| 232 PopulateKey(slot.Elem().Interface(), k) | 222 PopulateKey(slot.Elem().Interface(), k) |
| 233 }, | 223 }, |
| 234 } | 224 } |
| 235 } | 225 } |
| 236 | 226 |
| 237 func newKeyObjErr(aid, ns string, src interface{}) (*Key, error) { | 227 // multiArgTypeKeyExtraction == *Key |
| 238 » pls := getMGS(src) | 228 // |
| 239 » if key, _ := GetMetaDefault(pls, "key", nil).(*Key); key != nil { | 229 // This ONLY implements getKey. |
| 230 func multiArgTypeKeyExtraction() *multiArgType { | |
| 231 » return &multiArgType{ | |
| 232 » » getKey: func(aid, ns string, slot reflect.Value) (*Key, error) { | |
| 233 » » » return slot.Interface().(*Key), nil | |
| 234 » » }, | |
| 235 » } | |
| 236 } | |
| 237 | |
| 238 func newKeyObjErr(aid, ns string, mgs MetaGetterSetter) (*Key, error) { | |
| 239 » if key, _ := GetMetaDefault(mgs, "key", nil).(*Key); key != nil { | |
| 240 return key, nil | 240 return key, nil |
| 241 } | 241 } |
| 242 | 242 |
| 243 // get kind | 243 // get kind |
| 244 » kind := GetMetaDefault(pls, "kind", "").(string) | 244 » kind := GetMetaDefault(mgs, "kind", "").(string) |
| 245 if kind == "" { | 245 if kind == "" { |
| 246 » » return nil, fmt.Errorf("unable to extract $kind from %T", src) | 246 » » return nil, errors.New("unable to extract $kind") |
| 247 } | 247 } |
| 248 | 248 |
| 249 // get id - allow both to be default for default keys | 249 // get id - allow both to be default for default keys |
| 250 » sid := GetMetaDefault(pls, "id", "").(string) | 250 » sid := GetMetaDefault(mgs, "id", "").(string) |
| 251 » iid := GetMetaDefault(pls, "id", 0).(int64) | 251 » iid := GetMetaDefault(mgs, "id", 0).(int64) |
| 252 | 252 |
| 253 // get parent | 253 // get parent |
| 254 » par, _ := GetMetaDefault(pls, "parent", nil).(*Key) | 254 » par, _ := GetMetaDefault(mgs, "parent", nil).(*Key) |
| 255 | 255 |
| 256 return NewKey(aid, ns, kind, sid, iid, par), nil | 256 return NewKey(aid, ns, kind, sid, iid, par), nil |
| 257 } | 257 } |
| 258 | 258 |
| 259 func mkPLS(o interface{}) PropertyLoadSaver { | 259 func mkPLS(o interface{}) PropertyLoadSaver { |
| 260 if pls, ok := o.(PropertyLoadSaver); ok { | 260 if pls, ok := o.(PropertyLoadSaver); ok { |
| 261 return pls | 261 return pls |
| 262 } | 262 } |
| 263 return GetPLS(o) | 263 return GetPLS(o) |
| 264 } | 264 } |
| 265 | |
| 266 func isOKSingleType(t reflect.Type, allowKeys bool) error { | |
| 267 switch { | |
| 268 case t == nil: | |
| 269 return errors.New("no type information") | |
| 270 case t.Implements(typeOfPropertyLoadSaver): | |
| 271 return nil | |
| 272 case !allowKeys && t == typeOfKey: | |
| 273 return errors.New("not user datatype") | |
| 274 case t.Kind() != reflect.Ptr: | |
| 275 return errors.New("not a pointer") | |
| 276 case t.Elem().Kind() != reflect.Struct: | |
| 277 return errors.New("does not point to a struct") | |
| 278 default: | |
| 279 return nil | |
| 280 } | |
| 281 } | |
| 282 | |
| 283 type metaMultiArgElement struct { | |
| 284 arg reflect.Value | |
| 285 mat *multiArgType | |
| 286 size int // size is -1 if this element is not a slice. | |
| 287 } | |
| 288 | |
| 289 type metaMultiArg struct { | |
| 290 elems []metaMultiArgElement | |
| 291 keysOnly bool | |
| 292 | |
| 293 count int // total number of elements, flattening slices | |
| 294 } | |
| 295 | |
| 296 func makeMetaMultiArg(args []interface{}, keysOnly bool) (*metaMultiArg, error) { | |
| 297 mma := metaMultiArg{ | |
| 298 elems: make([]metaMultiArgElement, len(args)), | |
| 299 keysOnly: keysOnly, | |
| 300 } | |
| 301 | |
| 302 lme := errors.NewLazyMultiError(len(args)) | |
| 303 for i, arg := range args { | |
| 304 if arg == nil { | |
| 305 lme.Assign(i, errors.New("cannot use nil as single argum ent")) | |
| 306 continue | |
| 307 } | |
| 308 | |
| 309 v := reflect.ValueOf(arg) | |
| 310 vt := v.Type() | |
| 311 mma.elems[i].arg = v | |
| 312 | |
| 313 // Try and treat the argument as a single-value first. This allo ws slices | |
| 314 // that implement PropertyLoadSaver to be properly treated as a single | |
| 315 // element. | |
| 316 var err error | |
| 317 isSlice := false | |
| 318 mat := parseArg(vt, keysOnly) | |
| 319 if mat == nil { | |
| 320 // If this is a slice, treat it as a slice of arg candid ates. | |
| 321 if v.Kind() == reflect.Slice { | |
| 322 isSlice = true | |
| 323 mat = parseArg(vt.Elem(), keysOnly) | |
| 324 } | |
| 325 } else { | |
| 326 // Single types need to be able to be assigned to. | |
| 327 err = isOKSingleType(vt, keysOnly) | |
| 328 } | |
| 329 if mat == nil { | |
| 330 err = errors.New("not a PLS or pointer-to-struct") | |
| 331 } | |
| 332 if err != nil { | |
| 333 lme.Assign(i, fmt.Errorf("invalid input type (%T): %s", arg, err)) | |
| 334 continue | |
| 335 } | |
| 336 | |
| 337 mma.elems[i].mat = mat | |
| 338 if isSlice { | |
| 339 l := v.Len() | |
| 340 mma.count += l | |
| 341 mma.elems[i].size = l | |
| 342 } else { | |
| 343 mma.count++ | |
| 344 mma.elems[i].size = -1 | |
| 345 } | |
| 346 } | |
| 347 if err := lme.Get(); err != nil { | |
| 348 return nil, err | |
| 349 } | |
| 350 | |
| 351 return &mma, nil | |
| 352 } | |
| 353 | |
| 354 func (mma *metaMultiArg) iterator(cb metaMultiArgIteratorCallback) *metaMultiArg Iterator { | |
| 355 return &metaMultiArgIterator{ | |
| 356 metaMultiArg: mma, | |
| 357 cb: cb, | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 // getKeysPMs returns the | |
| 362 func (mma *metaMultiArg) getKeysPMs(aid, ns string, meta bool) ([]*Key, []Proper tyMap, error) { | |
| 363 var et errorTracker | |
| 364 it := mma.iterator(et.init(mma)) | |
| 365 | |
| 366 // Determine our flattened keys and property maps. | |
| 367 retKey := make([]*Key, mma.count) | |
| 368 var retPM []PropertyMap | |
| 369 if !mma.keysOnly { | |
| 370 retPM = make([]PropertyMap, mma.count) | |
| 371 } | |
| 372 | |
| 373 for i := 0; i < mma.count; i++ { | |
| 374 it.next(func(mat *multiArgType, slot reflect.Value) error { | |
| 375 key, err := mat.getKey(aid, ns, slot) | |
| 376 if err != nil { | |
| 377 return err | |
| 378 } | |
| 379 retKey[i] = key | |
| 380 | |
| 381 if !mma.keysOnly { | |
| 382 var pm PropertyMap | |
| 383 if meta { | |
| 384 pm = mat.getMetaPM(slot) | |
| 385 } else { | |
| 386 var err error | |
| 387 if pm, err = mat.getPM(slot); err != nil { | |
| 388 return err | |
| 389 } | |
| 390 } | |
| 391 retPM[i] = pm | |
| 392 } | |
| 393 return nil | |
| 394 }) | |
| 395 } | |
| 396 return retKey, retPM, et.error() | |
| 397 } | |
| 398 | |
| 399 type metaMultiArgIterator struct { | |
| 400 *metaMultiArg | |
| 401 | |
| 402 cb metaMultiArgIteratorCallback | |
| 403 | |
| 404 index int // flattened index | |
| 405 elemIdx int // current index in slice | |
| 406 slotIdx int // current index within elemIdx element (0 if single) | |
| 407 } | |
| 408 | |
| 409 func (mac *metaMultiArgIterator) next(fn func(*multiArgType, reflect.Value) erro r) { | |
|
iannucci
2016/05/28 02:50:53
s/mac/it ?
dnj
2016/05/28 17:47:21
Done.
| |
| 410 if mac.remaining() <= 0 { | |
| 411 panic("out of bounds") | |
| 412 } | |
| 413 | |
| 414 // Advance to the next populated element/slot. | |
| 415 elem := &mac.elems[mac.elemIdx] | |
| 416 if mac.index > 0 { | |
| 417 for { | |
| 418 mac.slotIdx++ | |
| 419 if mac.slotIdx >= elem.size { | |
| 420 mac.elemIdx++ | |
| 421 mac.slotIdx = 0 | |
| 422 elem = &mac.elems[mac.elemIdx] | |
| 423 } | |
| 424 | |
| 425 // We're done iterating, unless we're on a zero-sized sl ice element. | |
| 426 if elem.size != 0 { | |
| 427 break | |
| 428 } | |
| 429 } | |
| 430 } | |
| 431 | |
| 432 // Get the current slot value. | |
| 433 slot := elem.arg | |
| 434 if elem.size >= 0 { | |
| 435 // slot is a slice type, get its member. | |
| 436 slot = slot.Index(mac.slotIdx) | |
| 437 } | |
| 438 | |
| 439 // Execute our callback. | |
| 440 mac.cb(mac, fn(elem.mat, slot)) | |
| 441 | |
| 442 // Advance our flattened index. | |
| 443 mac.index++ | |
| 444 } | |
| 445 | |
| 446 func (mac *metaMultiArgIterator) remaining() int { | |
| 447 return mac.count - mac.index | |
| 448 } | |
| 449 | |
| 450 type metaMultiArgIteratorCallback func(*metaMultiArgIterator, error) | |
| 451 | |
| 452 type errorTracker struct { | |
| 453 elemErrors errors.LazyMultiError | |
| 454 sliceErrors map[int]errors.LazyMultiError | |
| 455 } | |
| 456 | |
| 457 func (et *errorTracker) init(mma *metaMultiArg) metaMultiArgIteratorCallback { | |
| 458 et.elemErrors = errors.NewLazyMultiError(len(mma.elems)) | |
| 459 return et.trackError | |
| 460 } | |
| 461 | |
| 462 func (et *errorTracker) trackError(it *metaMultiArgIterator, err error) { | |
| 463 if err == nil { | |
| 464 return | |
| 465 } | |
| 466 | |
| 467 // If this is a single element, assign the error directly. | |
| 468 elem := it.elems[it.elemIdx] | |
| 469 if elem.size < 0 { | |
| 470 et.elemErrors.Assign(it.elemIdx, err) | |
| 471 } else { | |
| 472 // This is a slice element. Use a slice-sized MultiError for its element | |
| 473 // error slot, then add this error to the inner MultiError's slo t index. | |
| 474 ilme := et.sliceErrors[it.elemIdx] | |
| 475 if ilme == nil { | |
| 476 ilme = errors.NewLazyMultiError(elem.size) | |
| 477 | |
| 478 if et.sliceErrors == nil { | |
| 479 et.sliceErrors = make(map[int]errors.LazyMultiEr ror) | |
|
iannucci
2016/05/28 02:50:53
let's just allocate the full 1st dimension map on
dnj
2016/05/28 17:47:21
Am building in-place. There is a bit of reflection
| |
| 480 } | |
| 481 et.sliceErrors[it.elemIdx] = ilme | |
| 482 } | |
| 483 ilme.Assign(it.slotIdx, err) | |
| 484 } | |
| 485 } | |
| 486 | |
| 487 func (et *errorTracker) error() error { | |
| 488 for i, lme := range et.sliceErrors { | |
| 489 if lerr := lme.Get(); lerr != nil { | |
| 490 et.elemErrors.Assign(i, lerr) | |
| 491 } | |
| 492 } | |
| 493 et.sliceErrors = nil | |
| 494 return et.elemErrors.Get() | |
| 495 } | |
| 496 | |
| 497 type boolTracker struct { | |
| 498 errorTracker | |
| 499 | |
| 500 res ExistsResult | |
| 501 } | |
| 502 | |
| 503 func (bt *boolTracker) init(mma *metaMultiArg) metaMultiArgIteratorCallback { | |
| 504 bt.errorTracker.init(mma) | |
| 505 | |
| 506 sizes := make([]int, len(mma.elems)) | |
| 507 for i, e := range mma.elems { | |
| 508 if e.size < 0 { | |
| 509 sizes[i] = 1 | |
| 510 } else { | |
| 511 sizes[i] = e.size | |
| 512 } | |
| 513 } | |
| 514 | |
| 515 bt.res.init(sizes...) | |
| 516 return bt.trackExistsResult | |
| 517 } | |
| 518 | |
| 519 func (bt *boolTracker) trackExistsResult(it *metaMultiArgIterator, err error) { | |
| 520 switch err { | |
| 521 case nil: | |
| 522 bt.res.set(it.elemIdx, it.slotIdx) | |
| 523 | |
| 524 case ErrNoSuchEntity: | |
| 525 break | |
| 526 | |
| 527 default: | |
| 528 // Pass through to track as MultiError. | |
| 529 bt.errorTracker.trackError(it, err) | |
| 530 } | |
| 531 } | |
| 532 | |
| 533 func (bt *boolTracker) result() *ExistsResult { | |
| 534 bt.res.updateSlices() | |
| 535 return &bt.res | |
| 536 } | |
| OLD | NEW |