1use fhir_diff::{Node, Pool, load};
4
5fn find<'a>(node: &'a Node, parts: &[&str]) -> Option<&'a Node> {
6 let Some((head, rest)) = parts.split_first() else {
7 return Some(node);
8 };
9 let (name, slice) = head
10 .split_once(':')
11 .map_or((*head, None), |(n, s)| (n, Some(s)));
12 let name = name.trim_end_matches("[x]");
13 let child = node.children.get(name)?;
14 let child = match slice {
15 Some(s) => child.slices.get(s)?,
16 None => child,
17 };
18 find(child, rest)
19}
20
21fn main() {
22 let spec = std::env::args().nth(1).expect("package");
23 let loaded = load(&spec).expect("load");
24 let pool = Pool::new(&loaded);
25 let (mut checked, mut differ) = (0, 0);
26 for profile in pool.profiles() {
27 let Some(definition) = loaded
28 .package
29 .definitions
30 .iter()
31 .find(|d| d.file == profile.file)
32 else {
33 continue;
34 };
35 for element in definition.json["snapshot"]["element"]
36 .as_array()
37 .into_iter()
38 .flatten()
39 {
40 let id = element["id"].as_str().unwrap_or_default();
41 let parts: Vec<&str> = id.split('.').skip(1).collect();
42 if parts.is_empty() || parts.iter().any(|p| p.contains("[x]:")) {
43 continue;
44 }
45 let Some(node) = find(&profile.tree, &parts) else {
46 continue;
47 };
48 checked += 1;
49 let min = element["min"].as_u64().unwrap_or(0) as u32;
50 let max = match element["max"].as_str() {
51 Some("*") => None,
52 Some(m) => m.parse().ok(),
53 None => None,
54 };
55 let ms = element["mustSupport"].as_bool().unwrap_or(false);
56 if node.min != min || node.max != max || node.must_support != ms {
57 differ += 1;
58 if differ <= 25 {
59 println!(
60 "{} {id}: tree {}..{:?} ms={} / snapshot {min}..{max:?} ms={ms}",
61 profile.name, node.min, node.max, node.must_support
62 );
63 }
64 }
65 }
66 }
67 println!("checked {checked} elements, {differ} differ");
68}