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