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 |