]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/config/merge.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / config / merge.go
1 package config
2
3 // Merge merges two configurations into a single configuration.
4 //
5 // Merge allows for the two configurations to have duplicate resources,
6 // because the resources will be merged. This differs from a single
7 // Config which must only have unique resources.
8 func Merge(c1, c2 *Config) (*Config, error) {
9 c := new(Config)
10
11 // Merge unknown keys
12 unknowns := make(map[string]struct{})
13 for _, k := range c1.unknownKeys {
14 _, present := unknowns[k]
15 if !present {
16 unknowns[k] = struct{}{}
17 c.unknownKeys = append(c.unknownKeys, k)
18 }
19 }
20 for _, k := range c2.unknownKeys {
21 _, present := unknowns[k]
22 if !present {
23 unknowns[k] = struct{}{}
24 c.unknownKeys = append(c.unknownKeys, k)
25 }
26 }
27
28 // Merge Atlas configuration. This is a dumb one overrides the other
29 // sort of merge.
30 c.Atlas = c1.Atlas
31 if c2.Atlas != nil {
32 c.Atlas = c2.Atlas
33 }
34
35 // Merge the Terraform configuration
36 if c1.Terraform != nil {
37 c.Terraform = c1.Terraform
38 if c2.Terraform != nil {
39 c.Terraform.Merge(c2.Terraform)
40 }
41 } else {
42 c.Terraform = c2.Terraform
43 }
44
45 // NOTE: Everything below is pretty gross. Due to the lack of generics
46 // in Go, there is some hoop-jumping involved to make this merging a
47 // little more test-friendly and less repetitive. Ironically, making it
48 // less repetitive involves being a little repetitive, but I prefer to
49 // be repetitive with things that are less error prone than things that
50 // are more error prone (more logic). Type conversions to an interface
51 // are pretty low-error.
52
53 var m1, m2, mresult []merger
54
55 // Modules
56 m1 = make([]merger, 0, len(c1.Modules))
57 m2 = make([]merger, 0, len(c2.Modules))
58 for _, v := range c1.Modules {
59 m1 = append(m1, v)
60 }
61 for _, v := range c2.Modules {
62 m2 = append(m2, v)
63 }
64 mresult = mergeSlice(m1, m2)
65 if len(mresult) > 0 {
66 c.Modules = make([]*Module, len(mresult))
67 for i, v := range mresult {
68 c.Modules[i] = v.(*Module)
69 }
70 }
71
72 // Outputs
73 m1 = make([]merger, 0, len(c1.Outputs))
74 m2 = make([]merger, 0, len(c2.Outputs))
75 for _, v := range c1.Outputs {
76 m1 = append(m1, v)
77 }
78 for _, v := range c2.Outputs {
79 m2 = append(m2, v)
80 }
81 mresult = mergeSlice(m1, m2)
82 if len(mresult) > 0 {
83 c.Outputs = make([]*Output, len(mresult))
84 for i, v := range mresult {
85 c.Outputs[i] = v.(*Output)
86 }
87 }
88
89 // Provider Configs
90 m1 = make([]merger, 0, len(c1.ProviderConfigs))
91 m2 = make([]merger, 0, len(c2.ProviderConfigs))
92 for _, v := range c1.ProviderConfigs {
93 m1 = append(m1, v)
94 }
95 for _, v := range c2.ProviderConfigs {
96 m2 = append(m2, v)
97 }
98 mresult = mergeSlice(m1, m2)
99 if len(mresult) > 0 {
100 c.ProviderConfigs = make([]*ProviderConfig, len(mresult))
101 for i, v := range mresult {
102 c.ProviderConfigs[i] = v.(*ProviderConfig)
103 }
104 }
105
106 // Resources
107 m1 = make([]merger, 0, len(c1.Resources))
108 m2 = make([]merger, 0, len(c2.Resources))
109 for _, v := range c1.Resources {
110 m1 = append(m1, v)
111 }
112 for _, v := range c2.Resources {
113 m2 = append(m2, v)
114 }
115 mresult = mergeSlice(m1, m2)
116 if len(mresult) > 0 {
117 c.Resources = make([]*Resource, len(mresult))
118 for i, v := range mresult {
119 c.Resources[i] = v.(*Resource)
120 }
121 }
122
123 // Variables
124 m1 = make([]merger, 0, len(c1.Variables))
125 m2 = make([]merger, 0, len(c2.Variables))
126 for _, v := range c1.Variables {
127 m1 = append(m1, v)
128 }
129 for _, v := range c2.Variables {
130 m2 = append(m2, v)
131 }
132 mresult = mergeSlice(m1, m2)
133 if len(mresult) > 0 {
134 c.Variables = make([]*Variable, len(mresult))
135 for i, v := range mresult {
136 c.Variables[i] = v.(*Variable)
137 }
138 }
139
140 return c, nil
141 }
142
143 // merger is an interface that must be implemented by types that are
144 // merge-able. This simplifies the implementation of Merge for the various
145 // components of a Config.
146 type merger interface {
147 mergerName() string
148 mergerMerge(merger) merger
149 }
150
151 // mergeSlice merges a slice of mergers.
152 func mergeSlice(m1, m2 []merger) []merger {
153 r := make([]merger, len(m1), len(m1)+len(m2))
154 copy(r, m1)
155
156 m := map[string]struct{}{}
157 for _, v2 := range m2 {
158 // If we already saw it, just append it because its a
159 // duplicate and invalid...
160 name := v2.mergerName()
161 if _, ok := m[name]; ok {
162 r = append(r, v2)
163 continue
164 }
165 m[name] = struct{}{}
166
167 // Find an original to override
168 var original merger
169 originalIndex := -1
170 for i, v := range m1 {
171 if v.mergerName() == name {
172 originalIndex = i
173 original = v
174 break
175 }
176 }
177
178 var v merger
179 if original == nil {
180 v = v2
181 } else {
182 v = original.mergerMerge(v2)
183 }
184
185 if originalIndex == -1 {
186 r = append(r, v)
187 } else {
188 r[originalIndex] = v
189 }
190 }
191
192 return r
193 }