@@ -47,6 +47,8 @@ func (m *FieldMapping) String() string {
47
47
sb .WriteString ("(field) of " )
48
48
}
49
49
50
+ sb .WriteString (m .fromNodeKey )
51
+
50
52
if m .to != "" {
51
53
sb .WriteString (" to " )
52
54
sb .WriteString (m .to )
@@ -60,20 +62,23 @@ func (m *FieldMapping) String() string {
60
62
// FromField creates a FieldMapping that maps a single predecessor field to the entire successor input.
61
63
// This is an exclusive mapping - once set, no other field mappings can be added since the successor input
62
64
// has already been fully mapped.
65
+ // Field: either the field of a struct, or the key of a map.
63
66
func FromField (from string ) * FieldMapping {
64
67
return & FieldMapping {
65
68
from : from ,
66
69
}
67
70
}
68
71
69
- // ToField creates a FieldMapping that maps the entire predecessor output to a single successor field
72
+ // ToField creates a FieldMapping that maps the entire predecessor output to a single successor field.
73
+ // Field: either the field of a struct, or the key of a map.
70
74
func ToField (to string ) * FieldMapping {
71
75
return & FieldMapping {
72
76
to : to ,
73
77
}
74
78
}
75
79
76
- // MapFields creates a FieldMapping that maps a single predecessor field to a single successor field
80
+ // MapFields creates a FieldMapping that maps a single predecessor field to a single successor field.
81
+ // Field: either the field of a struct, or the key of a map.
77
82
func MapFields (from , to string ) * FieldMapping {
78
83
return & FieldMapping {
79
84
from : from ,
@@ -160,6 +165,16 @@ func assignOne[T any](dest T, taken any, to string) (T, error) {
160
165
161
166
toSet := reflect .ValueOf (taken )
162
167
168
+ if destValue .Kind () == reflect .Map {
169
+ key , err := checkAndExtractToMapKey (to , destValue , toSet )
170
+ if err != nil {
171
+ return dest , err
172
+ }
173
+
174
+ destValue .SetMapIndex (key , toSet )
175
+ return destValue .Interface ().(T ), nil
176
+ }
177
+
163
178
field , err := checkAndExtractToField (to , destValue , toSet )
164
179
if err != nil {
165
180
return dest , err
@@ -171,30 +186,44 @@ func assignOne[T any](dest T, taken any, to string) (T, error) {
171
186
}
172
187
173
188
func checkAndExtractFromField (fromField string , input reflect.Value ) (reflect.Value , error ) {
174
- if input .Kind () == reflect .Ptr {
175
- input = input .Elem ()
176
- }
177
-
178
- if input .Kind () != reflect .Struct {
179
- return reflect.Value {}, fmt .Errorf ("mapping has from but input is not struct or struct ptr, type= %v" , input .Type ())
180
- }
181
-
182
189
f := input .FieldByName (fromField )
183
190
if ! f .IsValid () {
184
- return reflect.Value {}, fmt .Errorf ("mapping has from not found. field=%v, inputType=%v" , fromField , input .Type ())
191
+ return reflect.Value {}, fmt .Errorf ("field mapping from a struct field, but field not found. field=%v, inputType=%v" , fromField , input .Type ())
185
192
}
186
193
187
194
if ! f .CanInterface () {
188
- return reflect.Value {}, fmt .Errorf ("mapping has from not exported. field= %v, inputType=%v" , fromField , input .Type ())
195
+ return reflect.Value {}, fmt .Errorf ("field mapping from a struct field, but field not exported. field= %v, inputType=%v" , fromField , input .Type ())
189
196
}
190
197
191
198
return f , nil
192
199
}
193
200
201
+ func checkAndExtractFromMapKey (fromMapKey string , input reflect.Value ) (reflect.Value , error ) {
202
+ if ! reflect .TypeOf (fromMapKey ).AssignableTo (input .Type ().Key ()) {
203
+ return reflect.Value {}, fmt .Errorf ("field mapping from a map key, but input is not a map with string key, type=%v" , input .Type ())
204
+ }
205
+
206
+ v := input .MapIndex (reflect .ValueOf (fromMapKey ))
207
+ if ! v .IsValid () {
208
+ return reflect.Value {}, fmt .Errorf ("field mapping from a map key, but key not found in input. key=%s, inputType= %v" , fromMapKey , input .Type ())
209
+ }
210
+
211
+ return v , nil
212
+ }
213
+
194
214
func checkAndExtractFieldType (field string , typ reflect.Type ) (reflect.Type , error ) {
195
215
if len (field ) == 0 {
196
216
return typ , nil
197
217
}
218
+
219
+ if typ .Kind () == reflect .Map {
220
+ if typ .Key () != strType {
221
+ return nil , fmt .Errorf ("type[%v] is not a map with string key" , typ )
222
+ }
223
+
224
+ return typ .Elem (), nil
225
+ }
226
+
198
227
for typ .Kind () == reflect .Ptr {
199
228
typ = typ .Elem ()
200
229
}
@@ -215,31 +244,49 @@ func checkAndExtractFieldType(field string, typ reflect.Type) (reflect.Type, err
215
244
return f .Type , nil
216
245
}
217
246
247
+ var strType = reflect .TypeOf ("" )
248
+
218
249
func checkAndExtractToField (toField string , output , toSet reflect.Value ) (reflect.Value , error ) {
219
250
for output .Kind () == reflect .Ptr {
220
251
output = output .Elem ()
221
252
}
222
253
223
254
if output .Kind () != reflect .Struct {
224
- return reflect.Value {}, fmt .Errorf ("mapping has to but output is not a struct, type=%v" , output .Type ())
255
+ return reflect.Value {}, fmt .Errorf ("field mapping to a struct field but output is not a struct, type=%v" , output .Type ())
225
256
}
226
257
227
258
field := output .FieldByName (toField )
228
259
if ! field .IsValid () {
229
- return reflect.Value {}, fmt .Errorf ("mapping has to not found. field=%v, outputType=%v" , toField , output .Type ())
260
+ return reflect.Value {}, fmt .Errorf ("field mapping to a struct field, but field not found. field=%v, outputType=%v" , toField , output .Type ())
230
261
}
231
262
232
263
if ! field .CanSet () {
233
- return reflect.Value {}, fmt .Errorf ("mapping has to not exported. field=%v, outputType=%v" , toField , output .Type ())
264
+ return reflect.Value {}, fmt .Errorf ("field mapping to a struct field, but field not exported. field=%v, outputType=%v" , toField , output .Type ())
234
265
}
235
266
236
267
if ! toSet .Type ().AssignableTo (field .Type ()) {
237
- return reflect.Value {}, fmt .Errorf ("mapping to has a mismatched type. field=%s, from=%v, to=%v" , toField , toSet .Type (), field .Type ())
268
+ return reflect.Value {}, fmt .Errorf ("field mapping to a struct field, but field has a mismatched type. field=%s, from=%v, to=%v" , toField , toSet .Type (), field .Type ())
238
269
}
239
270
240
271
return field , nil
241
272
}
242
273
274
+ func checkAndExtractToMapKey (toMapKey string , output , toSet reflect.Value ) (reflect.Value , error ) {
275
+ if output .Kind () != reflect .Map {
276
+ return reflect.Value {}, fmt .Errorf ("field mapping to a map key but output is not a map, type=%v" , output .Type ())
277
+ }
278
+
279
+ if ! reflect .TypeOf (toMapKey ).AssignableTo (output .Type ().Key ()) {
280
+ return reflect.Value {}, fmt .Errorf ("field mapping to a map key but output is not a map with string key, type=%v" , output .Type ())
281
+ }
282
+
283
+ if ! toSet .Type ().AssignableTo (output .Type ().Elem ()) {
284
+ return reflect.Value {}, fmt .Errorf ("field mapping to a map key but map value has a mismatched type. key=%s, from=%v, to=%v" , toMapKey , toSet .Type (), output .Type ().Elem ())
285
+ }
286
+
287
+ return reflect .ValueOf (toMapKey ), nil
288
+ }
289
+
243
290
func fieldMap (mappings []* FieldMapping ) func (any ) (map [string ]any , error ) {
244
291
return func (input any ) (map [string ]any , error ) {
245
292
result := make (map [string ]any , len (mappings ))
@@ -262,14 +309,26 @@ func streamFieldMap(mappings []*FieldMapping) func(streamReader) streamReader {
262
309
}
263
310
}
264
311
265
- func takeOne (input any , from string ) (any , error ) {
312
+ func takeOne (input any , from string ) (taken any , err error ) {
266
313
if len (from ) == 0 {
267
314
return input , nil
268
315
}
269
316
270
317
inputValue := reflect .ValueOf (input )
271
318
272
- f , err := checkAndExtractFromField (from , inputValue )
319
+ var f reflect.Value
320
+ switch inputValue .Kind () {
321
+ case reflect .Map :
322
+ f , err = checkAndExtractFromMapKey (from , inputValue )
323
+ case reflect .Ptr :
324
+ inputValue = inputValue .Elem ()
325
+ fallthrough
326
+ case reflect .Struct :
327
+ f , err = checkAndExtractFromField (from , inputValue )
328
+ default :
329
+ return reflect.Value {}, fmt .Errorf ("field mapping from a field, but input is not struct, struct ptr or map, type= %v" , inputValue .Type ())
330
+ }
331
+
273
332
if err != nil {
274
333
return nil , err
275
334
}
@@ -295,11 +354,18 @@ func isToAll(mappings []*FieldMapping) bool {
295
354
return false
296
355
}
297
356
298
- func validateStruct (t reflect.Type ) bool {
299
- for t .Kind () == reflect .Ptr {
357
+ func validateStructOrMap (t reflect.Type ) bool {
358
+ switch t .Kind () {
359
+ case reflect .Map :
360
+ return true
361
+ case reflect .Ptr :
300
362
t = t .Elem ()
363
+ fallthrough
364
+ case reflect .Struct :
365
+ return true
366
+ default :
367
+ return false
301
368
}
302
- return t .Kind () != reflect .Struct
303
369
}
304
370
305
371
func validateFieldMapping (predecessorType reflect.Type , successorType reflect.Type , mappings []* FieldMapping ) (* handlerPair , error ) {
@@ -308,20 +374,25 @@ func validateFieldMapping(predecessorType reflect.Type, successorType reflect.Ty
308
374
// check if mapping is legal
309
375
if isFromAll (mappings ) && isToAll (mappings ) {
310
376
return nil , fmt .Errorf ("invalid field mappings: from all fields to all, use common edge instead" )
311
- } else if ! isToAll (mappings ) && validateStruct (successorType ) {
377
+ } else if ! isToAll (mappings ) && ! validateStructOrMap (successorType ) {
312
378
// if user has not provided a specific struct type, graph cannot construct any struct in the runtime
313
- return nil , fmt .Errorf ("static check fail: upstream input type should be struct, actual: %v" , successorType )
314
- } else if ! isFromAll (mappings ) && validateStruct (predecessorType ) {
379
+ return nil , fmt .Errorf ("static check fail: successor input type should be struct or map , actual: %v" , successorType )
380
+ } else if ! isFromAll (mappings ) && ! validateStructOrMap (predecessorType ) {
315
381
// TODO: should forbid?
316
- return nil , fmt .Errorf ("static check fail: downstream output type should be struct, actual: %v" , predecessorType )
382
+ return nil , fmt .Errorf ("static check fail: predecessor output type should be struct or map , actual: %v" , predecessorType )
317
383
}
318
384
385
+ var (
386
+ predecessorFieldType , successorFieldType reflect.Type
387
+ err error
388
+ )
389
+
319
390
for _ , mapping := range mappings {
320
- predecessorFieldType , err : = checkAndExtractFieldType (mapping .from , predecessorType )
391
+ predecessorFieldType , err = checkAndExtractFieldType (mapping .from , predecessorType )
321
392
if err != nil {
322
393
return nil , fmt .Errorf ("static check failed for mapping %s: %w" , mapping , err )
323
394
}
324
- successorFieldType , err : = checkAndExtractFieldType (mapping .to , successorType )
395
+ successorFieldType , err = checkAndExtractFieldType (mapping .to , successorType )
325
396
if err != nil {
326
397
return nil , fmt .Errorf ("static check failed for mapping %s: %w" , mapping , err )
327
398
}
0 commit comments