1use crate::snapshot::error::{SnapshotError, SnapshotResult};
2use crate::snapshot::path::ElementPath;
3use crate::snapshot::types::ElementDefinition;
4use std::collections::HashMap;
5use tracing::{debug, trace};
6
7pub struct ElementMerger;
8
9impl ElementMerger {
10 pub fn merge_elements(
11 base: &[ElementDefinition],
12 differential: &[ElementDefinition],
13 ) -> SnapshotResult<Vec<ElementDefinition>> {
14 debug!(
15 "Merging {} base elements with {} differential elements",
16 base.len(),
17 differential.len()
18 );
19
20 let mut element_map = Self::create_element_map(base);
21 Self::apply_differential(&mut element_map, differential)?;
22 Self::expand_slice_children(&mut element_map);
23
24 let mut result: Vec<ElementDefinition> = element_map.into_values().collect();
25 Self::sort_elements_by_path(&mut result);
26
27 debug!("Merge complete: {} elements in result", result.len());
28 Ok(result)
29 }
30
31 fn create_element_map(
32 base: &[ElementDefinition],
33 ) -> HashMap<(String, Option<String>), ElementDefinition> {
34 base.iter()
35 .map(|e| ((e.path.clone(), e.slice_name.clone()), e.clone()))
36 .collect()
37 }
38
39 fn apply_differential(
40 element_map: &mut HashMap<(String, Option<String>), ElementDefinition>,
41 differential: &[ElementDefinition],
42 ) -> SnapshotResult<()> {
43 for diff_element in differential {
44 let key = (diff_element.path.clone(), diff_element.slice_name.clone());
45
46 if let Some(existing) = element_map.get(&key) {
47 let merged = Self::merge_element(existing, diff_element)?;
48 element_map.insert(key, merged);
49 } else if diff_element.slice_name.is_some() {
50 let base_key = (diff_element.path.clone(), None);
51 if let Some(base_element) = element_map.get(&base_key) {
52 let merged = Self::merge_element(base_element, diff_element)?;
53 element_map.insert(key, merged);
54 } else {
55 element_map.insert(key, diff_element.clone());
56 }
57 } else {
58 element_map.insert(key, diff_element.clone());
59 }
60 }
61 Ok(())
62 }
63
64 fn sort_elements_by_path(elements: &mut [ElementDefinition]) {
65 elements.sort_by(|a, b| match a.path.cmp(&b.path) {
66 std::cmp::Ordering::Equal => match (&a.slice_name, &b.slice_name) {
67 (None, None) => std::cmp::Ordering::Equal,
68 (None, Some(_)) => std::cmp::Ordering::Less,
69 (Some(_), None) => std::cmp::Ordering::Greater,
70 (Some(a_name), Some(b_name)) => a_name.cmp(b_name),
71 },
72 other => other,
73 });
74 }
75
76 fn expand_slice_children(
77 element_map: &mut HashMap<(String, Option<String>), ElementDefinition>,
78 ) {
79 let mut new_elements = Vec::new();
80
81 let base_elements: Vec<(&String, ElementPath, &ElementDefinition)> = element_map
83 .iter()
84 .filter(|((_, sn), _)| sn.is_none())
85 .map(|((path, _), elem)| (path, ElementPath::new(path), elem))
86 .collect();
87
88 let slice_roots: Vec<(String, String)> = element_map
90 .keys()
91 .filter_map(|(path, slice_name)| {
92 slice_name.as_ref().map(|name| (path.clone(), name.clone()))
93 })
94 .collect();
95
96 for (slice_path_str, slice_name) in slice_roots {
97 let slice_base_path = ElementPath::new(&slice_path_str);
98
99 for (path, elem_path, elem) in &base_elements {
100 if elem_path.is_child_of(&slice_base_path) {
101 let slice_child_key = ((*path).clone(), Some(slice_name.clone()));
102
103 if !element_map.contains_key(&slice_child_key) {
104 let mut new_elem = (*elem).clone();
105 new_elem.slice_name = Some(slice_name.clone());
106 new_elements.push((slice_child_key, new_elem));
107 }
108 }
109 }
110 }
111
112 for (key, elem) in new_elements {
113 element_map.insert(key, elem);
114 }
115 }
116
117 fn merge_element(
118 base: &ElementDefinition,
119 diff: &ElementDefinition,
120 ) -> Result<ElementDefinition, SnapshotError> {
121 let (merged_min, merged_max) = Self::merge_cardinality(
122 base.min,
123 base.max.as_deref(),
124 diff.min,
125 diff.max.as_deref(),
126 &diff.path,
127 )?;
128
129 let merged_type = Self::merge_types(base.type_.as_ref(), diff.type_.as_ref(), &diff.path)?;
130
131 let merged_binding =
132 Self::merge_binding(base.binding.as_ref(), diff.binding.as_ref(), &diff.path)?;
133
134 let merged_constraints =
135 Self::merge_constraints(&base.constraint, &diff.constraint, &diff.path)?;
136
137 Ok(ElementDefinition {
138 path: diff.path.clone(),
139 id: diff.id.clone().or_else(|| base.id.clone()),
140 min: Some(merged_min),
141 max: Some(merged_max.to_string()),
142 type_: merged_type,
143 binding: merged_binding,
144 constraint: merged_constraints,
145 definition: diff.definition.clone().or_else(|| base.definition.clone()),
146 short: diff.short.clone().or_else(|| base.short.clone()),
147 comment: diff.comment.clone().or_else(|| base.comment.clone()),
148 requirements: diff
149 .requirements
150 .clone()
151 .or_else(|| base.requirements.clone()),
152 must_support: diff.must_support.or(base.must_support),
153 is_summary: diff.is_summary.or(base.is_summary),
154 is_modifier: diff.is_modifier.or(base.is_modifier),
155 is_modifier_reason: diff
156 .is_modifier_reason
157 .clone()
158 .or_else(|| base.is_modifier_reason.clone()),
159 slicing: diff.slicing.clone().or_else(|| base.slicing.clone()),
160 slice_name: diff.slice_name.clone().or_else(|| base.slice_name.clone()),
161 })
162 }
163
164 fn merge_cardinality(
165 base_min: Option<u32>,
166 base_max: Option<&str>,
167 diff_min: Option<u32>,
168 diff_max: Option<&str>,
169 path: &str,
170 ) -> Result<(u32, String), SnapshotError> {
171 let base_min = base_min.unwrap_or(0);
172 let base_max = base_max.unwrap_or("*");
173 let diff_min = diff_min.unwrap_or(base_min);
174 let diff_max = diff_max.unwrap_or(base_max);
175
176 if diff_min < base_min {
177 return Err(SnapshotError::MergeError(format!(
178 "Invalid cardinality for {path}: differential min ({diff_min}) is less than base min ({base_min})"
179 )));
180 }
181
182 let base_max_numeric = Self::parse_max_cardinality(base_max);
183 let diff_max_numeric = Self::parse_max_cardinality(diff_max);
184
185 match (base_max_numeric, diff_max_numeric) {
186 (Some(base_max_val), Some(diff_max_val)) => {
187 if diff_max_val > base_max_val {
188 return Err(SnapshotError::MergeError(format!(
189 "Invalid cardinality for {path}: differential max ({diff_max}) is greater than base max ({base_max})"
190 )));
191 }
192 }
193 (Some(_), None) => {
194 return Err(SnapshotError::MergeError(format!(
195 "Invalid cardinality for {path}: differential max ({diff_max}) is greater than base max ({base_max})"
196 )));
197 }
198 (None, _) => {}
199 }
200
201 if let Some(diff_max_val) = diff_max_numeric {
202 if diff_min > diff_max_val {
203 return Err(SnapshotError::MergeError(format!(
204 "Invalid cardinality for {path}: min ({diff_min}) is greater than max ({diff_max})"
205 )));
206 }
207 }
208
209 trace!(
210 "Merged cardinality for {}: {}..{} (from base {}..{}, diff {}..{})",
211 path,
212 diff_min,
213 diff_max,
214 base_min,
215 base_max,
216 diff_min,
217 diff_max
218 );
219
220 Ok((diff_min, diff_max.to_string()))
221 }
222
223 fn parse_max_cardinality(max: &str) -> Option<u32> {
224 if max == "*" {
225 None
226 } else {
227 max.parse::<u32>().ok()
228 }
229 }
230
231 fn merge_types(
232 base_types: Option<&Vec<crate::snapshot::types::ElementType>>,
233 diff_types: Option<&Vec<crate::snapshot::types::ElementType>>,
234 path: &str,
235 ) -> Result<Option<Vec<crate::snapshot::types::ElementType>>, SnapshotError> {
236 match (base_types, diff_types) {
237 (Some(base), Some(diff)) => {
238 for diff_type in diff {
239 let is_valid = base.iter().any(|base_type| {
240 if base_type.code != diff_type.code {
241 return false;
242 }
243 true
244 });
245
246 if !is_valid {
247 return Err(SnapshotError::MergeError(format!(
248 "Invalid type restriction for {path}: differential type '{}' is not in base types",
249 diff_type.code
250 )));
251 }
252 }
253
254 trace!("Merged types for {path}: {} differential types (restricted from {} base types)", diff.len(), base.len());
255 Ok(Some(diff.clone()))
256 }
257 (Some(base), None) => Ok(Some(base.clone())),
258 (None, Some(diff)) => Ok(Some(diff.clone())),
259 (None, None) => Ok(None),
260 }
261 }
262
263 fn merge_binding(
264 base_binding: Option<&crate::snapshot::types::ElementBinding>,
265 diff_binding: Option<&crate::snapshot::types::ElementBinding>,
266 path: &str,
267 ) -> Result<Option<crate::snapshot::types::ElementBinding>, SnapshotError> {
268 match (base_binding, diff_binding) {
269 (Some(base), Some(diff)) => {
270 let base_strength = Self::parse_binding_strength(&base.strength);
271 let diff_strength = Self::parse_binding_strength(&diff.strength);
272
273 if diff_strength < base_strength {
274 return Err(SnapshotError::MergeError(format!(
275 "Invalid binding for {path}: differential strength '{}' is weaker than base strength '{}'",
276 diff.strength, base.strength
277 )));
278 }
279
280 trace!(
281 "Merged binding for {path}: {} (from base {})",
282 diff.strength,
283 base.strength
284 );
285 Ok(Some(diff.clone()))
286 }
287 (Some(base), None) => Ok(Some(base.clone())),
288 (None, Some(diff)) => Ok(Some(diff.clone())),
289 (None, None) => Ok(None),
290 }
291 }
292
293 fn parse_binding_strength(strength: &str) -> u8 {
294 match strength.to_lowercase().as_str() {
295 "example" => 0,
296 "preferred" => 1,
297 "extensible" => 2,
298 "required" => 3,
299 _ => 0,
300 }
301 }
302
303 fn merge_constraints(
304 base: &Option<Vec<crate::snapshot::types::ElementConstraint>>,
305 diff: &Option<Vec<crate::snapshot::types::ElementConstraint>>,
306 path: &str,
307 ) -> Result<Option<Vec<crate::snapshot::types::ElementConstraint>>, SnapshotError> {
308 match (base, diff) {
309 (Some(base_constraints), Some(diff_constraints)) => {
310 let mut merged = base_constraints.clone();
311 let mut seen_keys: HashMap<String, String> = base_constraints
312 .iter()
313 .map(|c| (c.key.clone(), c.expression.clone().unwrap_or_default()))
314 .collect();
315
316 for diff_constraint in diff_constraints {
317 if let Some(existing_expr) = seen_keys.get(&diff_constraint.key) {
318 let diff_expr = diff_constraint.expression.as_deref().unwrap_or("");
319 if existing_expr != diff_expr {
320 return Err(SnapshotError::MergeError(format!(
321 "Duplicate constraint key '{}' for {path} with different expressions",
322 diff_constraint.key
323 )));
324 }
325 continue;
326 }
327 seen_keys.insert(
328 diff_constraint.key.clone(),
329 diff_constraint.expression.clone().unwrap_or_default(),
330 );
331 merged.push(diff_constraint.clone());
332 }
333
334 trace!("Merged constraints for {path}: {} total constraints (from {} base + {} differential)",
335 merged.len(), base_constraints.len(), diff_constraints.len());
336 Ok(Some(merged))
337 }
338 (Some(base_constraints), None) => Ok(Some(base_constraints.clone())),
339 (None, Some(diff_constraints)) => Ok(Some(diff_constraints.clone())),
340 (None, None) => Ok(None),
341 }
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348 use crate::snapshot::types::ElementDefinition;
349
350 fn create_element(path: &str, slice_name: Option<&str>) -> ElementDefinition {
351 ElementDefinition {
352 path: path.to_string(),
353 id: None,
354 min: None,
355 max: None,
356 type_: None,
357 binding: None,
358 constraint: None,
359 definition: None,
360 short: None,
361 comment: None,
362 requirements: None,
363 must_support: None,
364 is_summary: None,
365 is_modifier: None,
366 is_modifier_reason: None,
367 slicing: None,
368 slice_name: slice_name.map(|s| s.to_string()),
369 }
370 }
371
372 #[test]
373 fn test_merge_elements_basic() {
374 let base_elem = create_element("Patient", None);
375 let diff_elem = create_element("Patient", None);
376
377 let base = vec![base_elem];
378 let diff = vec![diff_elem];
379
380 let result = ElementMerger::merge_elements(&base, &diff).unwrap();
381 assert_eq!(result.len(), 1);
382 assert_eq!(result[0].path, "Patient");
383 }
384
385 #[test]
386 fn test_expand_slice_children() {
387 let mut map = HashMap::new();
388
389 let base_root = create_element("Patient.identifier", None);
391 let base_child = create_element("Patient.identifier.system", None);
392
393 map.insert(("Patient.identifier".to_string(), None), base_root);
394 map.insert(("Patient.identifier.system".to_string(), None), base_child);
395
396 let slice_root = create_element("Patient.identifier", Some("MRN"));
398 map.insert(
399 ("Patient.identifier".to_string(), Some("MRN".to_string())),
400 slice_root,
401 );
402
403 ElementMerger::expand_slice_children(&mut map);
404
405 assert!(map.contains_key(&(
407 "Patient.identifier.system".to_string(),
408 Some("MRN".to_string())
409 )));
410
411 let slice_child = map
412 .get(&(
413 "Patient.identifier.system".to_string(),
414 Some("MRN".to_string()),
415 ))
416 .unwrap();
417 assert_eq!(slice_child.slice_name.as_deref(), Some("MRN"));
418 }
419}