1use crate::{
2 AdditionalIncludes, ConvertPanicToException, CppRef, CppValue, LayoutPolicy, ZngurConstructor,
3 ZngurExternCppFn, ZngurExternCppImpl, ZngurField, ZngurFn, ZngurMethodDetails, ZngurSpec,
4 ZngurTrait, ZngurType,
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<std::collections::HashMap<K, V>> for I
92where
93 K: Eq + std::hash::Hash,
94 V: Merge,
95{
96 fn merge(self, into: &mut std::collections::HashMap<K, V>) -> MergeResult {
105 for (key, value) in self {
106 match into.entry(key) {
107 std::collections::hash_map::Entry::Vacant(e) => {
108 e.insert(value);
109 }
110 std::collections::hash_map::Entry::Occupied(mut e) => {
111 match value.merge(e.get_mut()) {
112 Ok(()) => {}
113 Err(message) => {
114 return Err(message);
115 }
116 }
117 }
118 }
119 }
120 Ok(())
121 }
122}
123
124impl Merge for ZngurType {
125 fn merge(self, into: &mut Self) -> MergeResult {
129 if self.ty != into.ty {
130 panic!(
131 "Attempt to merge different types: {} and {}",
132 self.ty, into.ty
133 );
134 }
135
136 if self.layout != into.layout {
137 return Err(MergeFailure::Conflict(
138 "Duplicate layout policy found".to_string(),
139 ));
140 }
141
142 if self.cpp_ref.is_some() && into.layout != LayoutPolicy::ZERO_SIZED_TYPE {
144 return Err(MergeFailure::Conflict(
145 "cpp_ref implies a zero sized stack allocated type".to_string(),
146 ));
147 }
148
149 self.cpp_value.merge(&mut into.cpp_value)?;
150 self.cpp_ref.merge(&mut into.cpp_ref)?;
151
152 inplace_union(self.wellknown_traits, &mut into.wellknown_traits);
153 merge_by_identity(self.methods, &mut into.methods, |a, b| {
154 a.data.name == b.data.name
155 })?;
156 merge_by_identity(self.constructors, &mut into.constructors, |a, b| {
157 a.name == b.name
158 })?;
159 merge_by_identity(self.fields, &mut into.fields, |a, b| a.name == b.name)?;
160
161 Ok(())
162 }
163}
164
165impl Merge for ZngurTrait {
166 fn merge(self, into: &mut Self) -> MergeResult {
170 if self.tr != into.tr {
171 panic!(
172 "Attempt to merge different traits: {} and {}",
173 self.tr, into.tr
174 );
175 }
176
177 inplace_union(self.methods, &mut into.methods);
178
179 Ok(())
180 }
181}
182
183impl Merge for CppValue {
184 fn merge(self, into: &mut Self) -> MergeResult {
189 if self != *into {
190 return Err(MergeFailure::Conflict("Cpp value mismatch".to_string()));
191 }
192 Ok(())
193 }
194}
195
196impl Merge for CppRef {
197 fn merge(self, into: &mut Self) -> MergeResult {
202 if self != *into {
203 return Err(MergeFailure::Conflict("Cpp ref mismatch".to_string()));
204 }
205 Ok(())
206 }
207}
208
209impl Merge<ZngurSpec> for ZngurType {
210 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
212 if let Some(existing) = into.types.iter_mut().find(|t| t.ty == self.ty) {
213 self.merge(existing)?;
214 } else {
215 into.types.push(self);
216 }
217 Ok(())
218 }
219}
220
221impl Merge for ZngurMethodDetails {
222 fn merge(self, into: &mut Self) -> MergeResult {
223 if self != *into {
224 return Err(MergeFailure::Conflict("Method mismatch".to_string()));
225 }
226 Ok(())
227 }
228}
229
230impl Merge for ZngurConstructor {
231 fn merge(self, into: &mut Self) -> MergeResult {
232 if self != *into {
233 return Err(MergeFailure::Conflict("Constructor mismatch".to_string()));
234 }
235 Ok(())
236 }
237}
238
239impl Merge for ZngurField {
240 fn merge(self, into: &mut Self) -> MergeResult {
241 if self != *into {
242 return Err(MergeFailure::Conflict("Field mismatch".to_string()));
243 }
244 Ok(())
245 }
246}
247
248impl Merge<ZngurSpec> for ZngurTrait {
249 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
251 [(self.tr.clone(), self)].merge(&mut into.traits)
252 }
253}
254
255impl Merge<ZngurSpec> for ZngurFn {
256 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
258 push_unique(self, &mut into.funcs);
259 Ok(())
260 }
261}
262
263impl Merge<ZngurSpec> for ZngurExternCppFn {
264 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
266 push_unique(self, &mut into.extern_cpp_funcs);
267 Ok(())
268 }
269}
270
271impl Merge<ZngurSpec> for ZngurExternCppImpl {
272 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
274 push_unique(self, &mut into.extern_cpp_impls);
275 Ok(())
276 }
277}
278
279impl Merge<ZngurSpec> for AdditionalIncludes {
280 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
282 into.additional_includes.0 += self.0.as_str();
283 Ok(())
284 }
285}
286
287impl Merge<ZngurSpec> for ConvertPanicToException {
288 fn merge(self, into: &mut ZngurSpec) -> MergeResult {
290 into.convert_panic_to_exception.0 |= self.0;
295 Ok(())
296 }
297}