github.com/avenga/couper@v1.12.2/eval/lib/merge.go (about) 1 package lib 2 3 import ( 4 "errors" 5 6 "github.com/zclconf/go-cty/cty" 7 "github.com/zclconf/go-cty/cty/function" 8 ) 9 10 var MergeFunc = newMergeFunction() 11 12 func newMergeFunction() function.Function { 13 return function.New(&function.Spec{ 14 Params: []function.Parameter{}, 15 VarParam: &function.Parameter{ 16 Name: "maps", 17 Type: cty.DynamicPseudoType, 18 AllowDynamicType: true, 19 AllowNull: true, 20 }, 21 Type: func(args []cty.Value) (cty.Type, error) { 22 // empty args is accepted, so assume an empty object since we have no 23 // key-value types. 24 if len(args) == 0 { 25 return cty.Bool, nil 26 } 27 return cty.DynamicPseudoType, nil 28 }, 29 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 30 return Merge(args) 31 }, 32 }) 33 } 34 35 func Merge(args []cty.Value) (cty.Value, error) { 36 var t string 37 for _, arg := range args { 38 if arg.IsNull() { 39 continue 40 } 41 at := arg.Type() 42 if at.IsPrimitiveType() { 43 return cty.StringVal(""), errors.New("cannot merge primitive value") 44 } 45 if at.IsObjectType() || at.IsMapType() { 46 if t == "" { 47 t = "o" 48 } else if t != "o" { 49 return cty.StringVal(""), errors.New("type mismatch") 50 } 51 } else if at.IsTupleType() || at.IsListType() { 52 if t == "" { 53 t = "l" 54 } else if t != "l" { 55 return cty.StringVal(""), errors.New("type mismatch") 56 } 57 } 58 } 59 if t == "o" { 60 return mergeObjects(args), nil 61 } 62 if t == "l" { 63 return mergeTuples(args), nil 64 } 65 return cty.NullVal(cty.Bool), nil 66 } 67 68 func mergeObjects(args []cty.Value) cty.Value { 69 outputMap := make(map[string]cty.Value) 70 for _, arg := range args { 71 if arg.IsNull() { 72 continue 73 } 74 75 for it := arg.ElementIterator(); it.Next(); { 76 k, v := it.Element() 77 if existingVal, ok := outputMap[k.AsString()]; !ok { 78 // key not set 79 outputMap[k.AsString()] = v 80 } else if vType := v.Type(); vType.IsPrimitiveType() { 81 // primitive type 82 outputMap[k.AsString()] = v 83 } else if existingValType := existingVal.Type(); existingValType.IsObjectType() && (vType.IsObjectType() || vType.IsMapType()) { 84 outputMap[k.AsString()] = mergeObjects([]cty.Value{existingVal, v}) 85 } else if existingValType.IsTupleType() && (vType.IsTupleType() || vType.IsListType()) { 86 outputMap[k.AsString()] = mergeTuples([]cty.Value{existingVal, v}) 87 } else { 88 outputMap[k.AsString()] = v 89 } 90 } 91 } 92 return cty.ObjectVal(outputMap) 93 } 94 95 func mergeTuples(args []cty.Value) cty.Value { 96 var outputList []cty.Value 97 for _, arg := range args { 98 if arg.IsNull() { 99 continue 100 } 101 for it := arg.ElementIterator(); it.Next(); { 102 _, v := it.Element() 103 outputList = append(outputList, v) 104 } 105 } 106 return cty.TupleVal(outputList) 107 }