1use crate::{
2 AdditionalIncludes, ConvertPanicToException, CppRef, CppStackOwned, CppValue, LayoutPolicy,
3 ZngurConstructor, ZngurExternCppFn, ZngurExternCppImpl, ZngurField, ZngurFn,
4 ZngurMethodDetails, ZngurSpec, ZngurTrait, ZngurType, ZngurVariant,
5};
6
7pub trait Merge<T = Self> {
23 fn merge(self, into: &mut T) -> MergeResult;
29}
30
31pub type MergeResult = Result<(), MergeFailure>;
33
34pub enum MergeFailure {
36 Conflict(String),
38}
39
40fn push_unique<T: Eq>(item: T, smallvec: &mut std::vec::Vec<T>) {
42 if !smallvec.contains(&item) {
43 smallvec.push(item);
44 }
45}
46
47fn inplace_union<T: Eq>(other: Vec<T>, smallvec: &mut std::vec::Vec<T>) {
49 for item in other {
50 push_unique(item, smallvec);
51 }
52}
53
54fn merge_by_identity<T: Merge>(
58 other: Vec<T>,
59 smallvec: &mut std::vec::Vec<T>,
60 identity: impl Fn(&T, &T) -> bool,
61) -> MergeResult {
62 for item in other {
63 if let Some(existing) = smallvec.iter_mut().find(|e| identity(e, &item)) {
64 item.merge(existing)?;
65 } else {
66 smallvec.push(item);
67 }
68 }
69 Ok(())
70}
71
72impl<T: Merge> Merge for Option<T> {
73 fn merge(self, into: &mut Self) -> MergeResult {
78 match self {
79 Some(src) => match into.as_mut() {
80 Some(dst) => src.merge(dst),
81 None => {
82 *into = Some(src);
83 Ok(())
84 }
85 },
86 None => Ok(()),
87 }
88 }
89}
90
91impl<K, V, I: IntoIterator<Item = (K, V)>> Merge<indexmap::IndexMap<K, V>> for I
92where
93 K: Eq + std::hash::Hash,
94 V: Merge,
95{
96 fn merge(self, into: &mut indexmap::IndexMap<K, V>) -> MergeResult {
105 for (key, value) in self {
106 match into.entry(key) {
107 indexmap::map::Entry::Vacant(e) => {
108 e.insert(value);
109 }
110 indexmap::map::Entry::Occupied(mut e) => match value.merge(e.get_mut()) {
111 Ok(()) => {}
112 Err(message) => {
113 return Err(message);
114 }
115 },
116 }
117 }
118 Ok(())
119 }
120}
121
122impl Merge for ZngurType {
123 fn merge(self, into: &mut Self) -> MergeResult {
127 if self.ty != into.ty {
128 panic!(
129 "Attempt to merge different types: {} and {}",
130 self.ty, into.ty
131 );
132 }
133
134 if let (Some(layout1), Some(layout2)) = &(self.layout, into.layout)
135 && layout1 != layout2
136 {
137 return Err(MergeFailure::Conflict(
138 "Conflicting layout policy found".to_string(),
139 ));
140 } else {
141 into.layout = into.layout.or(self.layout);
142 }
143
144 if self.cpp_ref.is_some() && into.layout != Some(LayoutPolicy::ZERO_SIZED_TYPE) {
146 return Err(MergeFailure::Conflict(
147 "cpp_ref implies a zero sized stack allocated type".to_string(),
148 ));
149 }
150
151 self.cpp_value.merge(&mut into.cpp_value)?;
152 self.cpp_ref.merge(&mut into.cpp_ref)?;
153 self.cpp_stack_owned.merge(&mut into.cpp_stack_owned)?;
154
155 inplace_union(self.wellknown_traits, &mut into.wellknown_traits);
156 merge_by_identity(self.methods, &mut into.methods, |a, b| {
157 a.data.name == b.data.name
158 })?;
159 if into.constructor.is_none() {
160 into.constructor = self.constructor;
161 } else if self.constructor != into.constructor {
162 return Err(MergeFailure::Conflict(
163 "Duplicate constructor found".to_string(),
164 ));
165 }
166 into.exhaustive = self.exhaustive && into.exhaustive;
167 merge_by_identity(self.variants, &mut into.variants, |a, b| a.name == b.name)?;
168 merge_by_identity(self.fields, &mut into.fields, |a, b| a.name == b.name)?;
169
170 Ok(())
171 }
172}
173
174impl Merge for ZngurTrait {
175 fn merge(self, into: &mut Self) -> MergeResult {
179 if self.tr != into.tr {
180 panic!(
181 "Attempt to merge different traits: {} and {}",
182 self.tr, into.tr
183 );
184 }
185
186 inplace_union(self.methods, &mut into.methods);
187
188 Ok(())
189 }
190}
191
192impl Merge for CppValue {
193 fn merge(self, into: &mut Self) -> MergeResult {
198 if self != *into {
199 return Err(MergeFailure::Conflict("Cpp value mismatch".to_string()));
200 }
201 Ok(())
202 }
203}
204
205impl Merge for CppRef {
206 fn merge(self, into: &mut Self) -> MergeResult {
211 if self != *into {
212 return Err(MergeFailure::Conflict("Cpp ref mismatch".to_string()));
213 }
214 Ok(())
215 }
216}
217
218impl Merge for CppStackOwned {
219 fn merge(self, into: &mut Self) -> MergeResult {
220 if self != *into {
221 return Err(MergeFailure::Conflict(
222 "Cpp stack owned mismatch".to_string(),
223 ));
224 }
225 Ok(())
226 }
227}
228
229impl Merge<ZngurSpec> for ZngurType {
230 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
232 if let Some(existing) = into.types.iter_mut().find(|t| t.ty == self.ty) {
233 self.merge(existing)?;
234 } else {
235 into.types.push(self);
236 }
237 Ok(())
238 }
239}
240
241impl Merge for ZngurMethodDetails {
242 fn merge(self, into: &mut Self) -> MergeResult {
243 if self != *into {
244 return Err(MergeFailure::Conflict("Method mismatch".to_string()));
245 }
246 Ok(())
247 }
248}
249
250impl Merge for ZngurConstructor {
251 fn merge(self, into: &mut Self) -> MergeResult {
252 if self != *into {
253 return Err(MergeFailure::Conflict("Constructor mismatch".to_string()));
254 }
255 Ok(())
256 }
257}
258
259impl Merge for ZngurVariant {
260 fn merge(self, into: &mut Self) -> MergeResult {
261 if self.name != into.name {
262 panic!(
263 "Attempt to merge different variants: {} and {}",
264 self.name, into.name
265 );
266 }
267 merge_by_identity(self.fields, &mut into.fields, |a, b| a.name == b.name)?;
268 into.exhaustive = self.exhaustive && into.exhaustive;
269 Ok(())
270 }
271}
272
273impl Merge for ZngurField {
274 fn merge(self, into: &mut Self) -> MergeResult {
275 if self != *into {
276 return Err(MergeFailure::Conflict("Field mismatch".to_string()));
277 }
278 Ok(())
279 }
280}
281
282impl Merge<ZngurSpec> for ZngurTrait {
283 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
285 [(self.tr.clone(), self)].merge(&mut into.traits)
286 }
287}
288
289impl Merge<ZngurSpec> for ZngurFn {
290 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
292 push_unique(self, &mut into.funcs);
293 Ok(())
294 }
295}
296
297impl Merge<ZngurSpec> for ZngurExternCppFn {
298 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
300 push_unique(self, &mut into.extern_cpp_funcs);
301 Ok(())
302 }
303}
304
305impl Merge<ZngurSpec> for ZngurExternCppImpl {
306 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
308 push_unique(self, &mut into.extern_cpp_impls);
309 Ok(())
310 }
311}
312
313impl Merge<ZngurSpec> for AdditionalIncludes {
314 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
316 into.additional_includes.0 += self.0.as_str();
317 Ok(())
318 }
319}
320
321impl Merge<ZngurSpec> for ConvertPanicToException {
322 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
324 into.convert_panic_to_exception.0 |= self.0;
329 Ok(())
330 }
331}