1use fml_check::{Lookup, Type, Types};
9
10pub const TYPE_KEY: &str = "@type";
13use serde_json::{Map, Value};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct NodeId(usize);
18
19#[derive(Debug, Clone)]
20struct Node {
21 ty: Type,
22 type_name: Option<String>,
24 resource: bool,
26 data: Data,
27}
28
29#[derive(Debug, Clone)]
30enum Data {
31 Primitive(Value, Vec<Property>),
34 Complex(Vec<Property>),
35}
36
37#[derive(Debug, Clone)]
38struct Property {
39 name: String,
41 choice: bool,
42 typed: bool,
45 declared: Option<String>,
47 many: bool,
48 index: Option<usize>,
50 values: Vec<NodeId>,
51}
52
53struct Slot {
55 name: String,
56 choice: bool,
57 typed: bool,
58 many: bool,
59 index: Option<usize>,
60 ty: Type,
62}
63
64#[derive(Debug, Default, Clone)]
65pub struct Tree {
66 nodes: Vec<Node>,
67 paths: std::collections::HashMap<NodeId, String>,
70}
71
72impl Tree {
73 pub fn record_paths(&mut self, root: NodeId, root_name: &str) {
76 let mut pending = vec![(root, root_name.to_owned())];
77 while let Some((node, path)) = pending.pop() {
78 for property in self.properties(node) {
79 for (index, &child) in property.values.iter().enumerate() {
80 let child_path = if property.many {
81 format!("{path}.{}[{index}]", property.name)
82 } else {
83 format!("{path}.{}", property.name)
84 };
85 pending.push((child, child_path));
86 }
87 }
88 self.paths.insert(node, path);
89 }
90 }
91
92 pub fn path(&self, id: NodeId) -> Option<&str> {
94 self.paths.get(&id).map(String::as_str)
95 }
96
97 pub fn len(&self) -> usize {
99 self.nodes.len()
100 }
101
102 fn push(&mut self, node: Node) -> NodeId {
103 self.nodes.push(node);
104 NodeId(self.nodes.len() - 1)
105 }
106
107 pub fn create(&mut self, ty: Type, type_name: Option<String>) -> NodeId {
109 let data = if ty.is_primitive() {
110 Data::Primitive(Value::Null, Vec::new())
111 } else {
112 Data::Complex(Vec::new())
113 };
114 self.push(Node {
115 resource: ty.is_resource(),
116 ty,
117 type_name,
118 data,
119 })
120 }
121
122 pub fn primitive(&mut self, value: Value, ty: Type, type_name: Option<String>) -> NodeId {
124 self.push(Node {
125 ty,
126 type_name,
127 resource: false,
128 data: Data::Primitive(value, Vec::new()),
129 })
130 }
131
132 pub fn fhir_type(&self, id: NodeId) -> Option<String> {
133 let node = &self.nodes[id.0];
134 node.ty.fhir_type().or_else(|| node.type_name.clone())
135 }
136
137 pub fn ty(&self, id: NodeId) -> &Type {
138 &self.nodes[id.0].ty
139 }
140
141 pub fn is_primitive(&self, id: NodeId) -> bool {
142 matches!(self.nodes[id.0].data, Data::Primitive(..))
143 }
144
145 pub fn value(&self, id: NodeId) -> Option<&Value> {
147 match &self.nodes[id.0].data {
148 Data::Primitive(value, _) => Some(value),
149 Data::Complex(_) => None,
150 }
151 }
152
153 pub fn text(&self, id: NodeId) -> Option<String> {
155 match self.value(id)? {
156 Value::String(s) => Some(s.clone()),
157 Value::Null => None,
158 other => Some(other.to_string()),
159 }
160 }
161
162 pub fn load(&mut self, json: &Value, ty: Type, types: &Types<'_>) -> NodeId {
166 let marks_resource = json.get("resourceType").is_some()
169 && !matches!(types.lookup(&ty, "resourceType"), Lookup::Found(_));
170 if !marks_resource && json.is_object() {
171 return self.load_object(json, ty, None, types);
172 }
173 let ty = match (&ty, json.get("resourceType").and_then(Value::as_str)) {
174 (_, Some(resource_type)) if ty.fhir_type().as_deref() != Some(resource_type) => {
176 let context = ty
177 .structure()
178 .map(|s| s.url.clone())
179 .unwrap_or_else(|| "http://hl7.org/fhir/StructureDefinition/".to_owned());
180 match types.structure(&fml_check::definitions::type_url(resource_type, &context)) {
181 Type::Unknown => ty,
182 known => known,
183 }
184 }
185 _ => ty,
186 };
187 let type_name = json
188 .get("resourceType")
189 .and_then(Value::as_str)
190 .map(str::to_owned);
191 if !json.is_object() {
192 return self.primitive(json.clone(), ty, type_name);
193 }
194 self.load_object(json, ty, type_name, types)
195 }
196
197 fn load_object(
199 &mut self,
200 json: &Value,
201 ty: Type,
202 resource_type: Option<String>,
203 types: &Types<'_>,
204 ) -> NodeId {
205 let Value::Object(object) = json else {
206 return self.primitive(json.clone(), ty, None);
207 };
208 let is_resource = resource_type.is_some();
209 let id = self.push(Node {
210 resource: is_resource || ty.is_resource(),
211 ty: ty.clone(),
212 type_name: resource_type,
213 data: Data::Complex(Vec::new()),
214 });
215 for (key, value) in object {
216 if key == TYPE_KEY {
217 continue;
218 }
219 if (is_resource && key == "resourceType")
220 || (key.starts_with('_') && object.contains_key(&key[1..]))
221 {
222 continue;
223 }
224 if let Some(base) = key.strip_prefix('_') {
226 let (slot, child_ty, _) = self.slot_for_key(&ty, base, types);
227 let elements: Vec<&Value> = match value {
228 Value::Array(items) => items.iter().collect(),
229 single => vec![single],
230 };
231 let children: Vec<NodeId> = elements
232 .into_iter()
233 .map(|element| {
234 let child = self.primitive(Value::Null, child_ty.clone(), None);
235 self.load_primitive_element(child, element, types);
236 child
237 })
238 .collect();
239 let many = slot.many || value.is_array();
240 self.properties_mut(id).push(Property {
241 name: slot.name,
242 choice: slot.choice,
243 typed: slot.typed,
244 declared: declared_key(&slot.ty),
245 many,
246 index: slot.index,
247 values: children,
248 });
249 continue;
250 }
251 let (slot, child_ty, element) = self.slot_for_key(&ty, key, types);
252 let values: Vec<&Value> = match value {
253 Value::Array(items) => items.iter().collect(),
254 single => vec![single],
255 };
256 let mut children = Vec::with_capacity(values.len());
257 for item in values {
258 let item_ty = match (item.get(TYPE_KEY).and_then(Value::as_str), &element) {
260 (Some(name), Some(element)) => match types.named_type(element, name) {
261 Type::Unknown => child_ty.clone(),
262 named => named,
263 },
264 _ => child_ty.clone(),
265 };
266 children.push(self.load(item, item_ty, types));
267 }
268 if let Some(elements) = object.get(&format!("_{key}")) {
270 let elements: Vec<&Value> = match elements {
271 Value::Array(items) => items.iter().collect(),
272 single => vec![single],
273 };
274 for (&child, element) in children.iter().zip(elements) {
275 self.load_primitive_element(child, element, types);
276 }
277 }
278 let many = slot.many || value.is_array();
279 self.properties_mut(id).push(Property {
280 name: slot.name,
281 choice: slot.choice,
282 typed: slot.typed,
283 declared: declared_key(&slot.ty),
284 many,
285 index: slot.index,
286 values: children,
287 });
288 }
289 id
290 }
291
292 fn load_primitive_element(&mut self, primitive: NodeId, element: &Value, types: &Types<'_>) {
294 if !element.is_object() || !self.is_primitive(primitive) {
295 return;
296 }
297 let holder = self.load(element, self.ty(primitive).clone(), types);
298 let properties = std::mem::take(self.properties_mut(holder));
299 *self.properties_mut(primitive) = properties;
300 }
301
302 fn slot_for_key(
304 &self,
305 ty: &Type,
306 key: &str,
307 types: &Types<'_>,
308 ) -> (Slot, Type, Option<fml_check::ElementRef>) {
309 if let Lookup::Found(found) = types.lookup(ty, key) {
310 if let Some(base) = &found.element.choice_of {
311 let base_slot = match types.lookup(ty, base) {
312 Lookup::Found(base_found) => element_order(ty, &base_found, types),
313 _ => element_order(ty, &found, types),
314 };
315 let child = types.element_type(&found, None);
316 return (
317 Slot {
318 name: base.clone(),
319 choice: true,
320 typed: false,
321 many: false,
322 index: base_slot,
323 ty: child.clone(),
324 },
325 child,
326 Some(*found),
327 );
328 }
329 let child = types.element_type(&found, None);
330 return (
331 Slot {
332 name: key.to_owned(),
333 choice: !found.element.choices.is_empty(),
334 typed: found.element.is_polymorphic(),
335 many: found.element.array,
336 index: element_order(ty, &found, types),
337 ty: child.clone(),
338 },
339 child,
340 Some(*found),
341 );
342 }
343 (
344 Slot {
345 name: key.to_owned(),
346 choice: false,
347 typed: false,
348 many: false,
349 index: None,
350 ty: Type::Unknown,
351 },
352 Type::Unknown,
353 None,
354 )
355 }
356
357 fn properties(&self, id: NodeId) -> &[Property] {
361 match &self.nodes[id.0].data {
362 Data::Complex(properties) | Data::Primitive(_, properties) => properties,
363 }
364 }
365
366 fn properties_mut(&mut self, id: NodeId) -> &mut Vec<Property> {
367 match &mut self.nodes[id.0].data {
368 Data::Complex(properties) | Data::Primitive(_, properties) => properties,
369 }
370 }
371
372 pub fn children(&self, id: NodeId, name: &str) -> Vec<NodeId> {
375 if name == "value" && self.is_primitive(id) {
376 return vec![id];
377 }
378 let mut out = Vec::new();
379 for property in self.properties(id) {
380 if property.name == name {
381 out.extend(&property.values);
382 } else if property.choice && name.starts_with(&property.name) {
383 for &value in &property.values {
384 let suffix = &name[property.name.len()..];
385 if self
386 .fhir_type(value)
387 .is_some_and(|t| capitalize(&t) == suffix)
388 {
389 out.push(value);
390 }
391 }
392 }
393 }
394 out
395 }
396
397 fn slot(&self, id: NodeId, name: &str, types: &Types<'_>) -> Result<Slot, String> {
401 let ty = self.ty(id).clone();
402 match types.lookup(&ty, name) {
403 Lookup::Found(found) => Ok(Slot {
404 name: name.to_owned(),
405 choice: !found.element.choices.is_empty(),
406 typed: found.element.is_polymorphic(),
407 many: found.element.array,
408 index: element_order(&ty, &found, types),
409 ty: types.element_type(&found, None),
410 }),
411 Lookup::Missing => Err(format!("`{}` has no element `{name}`", ty.display())),
412 Lookup::Unknown => {
413 let existing = self.properties(id).iter().find(|p| p.name == name);
416 Ok(Slot {
417 name: name.to_owned(),
418 choice: false,
419 typed: false,
420 many: existing.is_some_and(|p| p.many),
421 index: None,
422 ty: Type::Unknown,
423 })
424 }
425 }
426 }
427
428 pub fn make_property(
431 &mut self,
432 id: NodeId,
433 name: &str,
434 types: &Types<'_>,
435 ) -> Result<NodeId, String> {
436 if self.is_primitive(id) && name == "value" {
437 return Ok(id);
438 }
439 let slot = self.slot(id, name, types)?;
440 if slot.choice || slot.typed {
441 return Err(format!(
442 "`{}.{name}` can have several types; give the type with a transform such as create('Type')",
443 self.ty(id).display()
444 ));
445 }
446 if !slot.many
447 && let Some(existing) = self
448 .properties(id)
449 .iter()
450 .find(|p| p.name == name)
451 .and_then(|p| p.values.first().copied())
452 {
453 return Ok(existing);
454 }
455 let child = self.create(slot.ty.clone(), None);
456 self.attach(id, slot, child);
457 Ok(child)
458 }
459
460 pub fn set_property(
463 &mut self,
464 id: NodeId,
465 name: &str,
466 value: NodeId,
467 types: &Types<'_>,
468 ) -> Result<(), String> {
469 if self.is_primitive(id) && name == "value" {
471 let assigned = self.value(value).cloned().unwrap_or(Value::Null);
472 if let Data::Primitive(current, _) = &mut self.nodes[id.0].data {
473 *current = assigned;
474 }
475 return Ok(());
476 }
477 let slot = self.slot(id, name, types)?;
478 if (slot.choice || slot.typed) && self.fhir_type(value).is_none() {
479 return Err(format!(
480 "cannot tell which type of `{}.{name}` the value has",
481 self.ty(id).display()
482 ));
483 }
484 if !slot.choice && self.is_primitive(value) && !matches!(slot.ty, Type::Unknown) {
485 self.nodes[value.0].ty = slot.ty.clone();
486 }
487 if !slot.many {
488 self.properties_mut(id).retain(|p| p.name != name);
489 }
490 self.attach(id, slot, value);
491 Ok(())
492 }
493
494 fn attach(&mut self, id: NodeId, slot: Slot, child: NodeId) {
495 let properties = self.properties_mut(id);
496 match properties.iter_mut().find(|p| p.name == slot.name) {
497 Some(property) => property.values.push(child),
498 None => properties.push(Property {
499 declared: declared_key(&slot.ty),
500 name: slot.name,
501 choice: slot.choice,
502 typed: slot.typed,
503 many: slot.many,
504 index: slot.index,
505 values: vec![child],
506 }),
507 }
508 }
509
510 pub fn move_value(&mut self, id: NodeId, name: &str, value: NodeId, index: Option<usize>) {
512 let Some(property) = self
513 .properties_mut(id)
514 .iter_mut()
515 .find(|p| p.name == name && p.many)
516 else {
517 return;
518 };
519 let Some(at) = property.values.iter().position(|&v| v == value) else {
520 return;
521 };
522 property.values.remove(at);
523 let index = index.map_or(property.values.len(), |i| i.min(property.values.len()));
524 property.values.insert(index, value);
525 }
526
527 pub fn assign(&mut self, to: NodeId, from: NodeId) {
529 let copy = self.deep_copy(from);
530 let node = self.nodes[copy.0].clone();
531 let target = &mut self.nodes[to.0];
532 target.data = node.data;
533 if matches!(target.ty, Type::Unknown) {
534 target.ty = node.ty;
535 target.type_name = node.type_name;
536 }
537 }
538
539 pub fn deep_copy(&mut self, id: NodeId) -> NodeId {
541 let node = self.nodes[id.0].clone();
542 let copy_properties = |properties: Vec<Property>, tree: &mut Self| -> Vec<Property> {
543 properties
544 .into_iter()
545 .map(|property| Property {
546 values: property
547 .values
548 .iter()
549 .map(|&value| tree.deep_copy(value))
550 .collect(),
551 ..property
552 })
553 .collect()
554 };
555 let data = match node.data {
556 Data::Primitive(value, properties) => {
557 Data::Primitive(value, copy_properties(properties, self))
558 }
559 Data::Complex(properties) => Data::Complex(copy_properties(properties, self)),
560 };
561 self.push(Node { data, ..node })
562 }
563
564 pub fn set_text(
566 &mut self,
567 id: NodeId,
568 name: &str,
569 text: &str,
570 types: &Types<'_>,
571 ) -> Result<(), String> {
572 let value = self.primitive(
573 Value::String(text.to_owned()),
574 Type::Unknown,
575 Some("string".to_owned()),
576 );
577 self.set_property(id, name, value, types)
578 }
579
580 pub fn reachable(&self, root: NodeId) -> std::collections::HashSet<NodeId> {
582 let mut seen = std::collections::HashSet::new();
583 let mut stack = vec![root];
584 while let Some(node) = stack.pop() {
585 if seen.insert(node) {
586 for property in self.properties(node) {
587 stack.extend(&property.values);
588 }
589 }
590 }
591 seen
592 }
593
594 pub fn to_json(&self, id: NodeId) -> Value {
598 let node = &self.nodes[id.0];
599 match &node.data {
600 Data::Primitive(value, _) => value.clone(),
601 Data::Complex(properties) => {
602 let mut object = Map::new();
603 if (node.resource || node.ty.is_resource())
604 && let Some(name) = self.fhir_type(id)
605 {
606 object.insert("resourceType".to_owned(), Value::String(name));
607 }
608 self.write_properties(properties, &mut object);
609 Value::Object(object)
610 }
611 }
612 }
613
614 fn name_type(&self, property: &Property, value: NodeId, json: &mut Value) {
617 let node = &self.nodes[value.0];
618 if node.resource {
619 return;
620 }
621 let Value::Object(object) = json else {
622 return;
623 };
624 let differs = property.declared.as_ref().is_some_and(|declared| {
625 !matches!(node.ty, Type::Unknown) && *declared != node.ty.key()
626 });
627 if !(property.typed || differs) {
628 return;
629 }
630 let Some(name) = type_label(&node.ty).or_else(|| node.type_name.clone()) else {
631 return;
632 };
633 let mut named = Map::with_capacity(object.len() + 1);
634 named.insert(TYPE_KEY.to_owned(), Value::String(name));
635 named.extend(std::mem::take(object));
636 *object = named;
637 }
638
639 fn primitive_element(&self, id: NodeId) -> Option<Value> {
641 match &self.nodes[id.0].data {
642 Data::Primitive(_, properties) if !properties.is_empty() => {
643 let mut object = Map::new();
644 self.write_properties(properties, &mut object);
645 (!object.is_empty()).then_some(Value::Object(object))
646 }
647 _ => None,
648 }
649 }
650
651 fn write_properties(&self, properties: &[Property], object: &mut Map<String, Value>) {
652 let mut ordered: Vec<&Property> = properties.iter().collect();
653 ordered.sort_by_key(|p| p.index.unwrap_or(usize::MAX));
654 for property in ordered {
655 let values: Vec<(NodeId, Value, Option<Value>)> = property
656 .values
657 .iter()
658 .map(|&value| {
659 let mut json = self.to_json(value);
660 if !property.choice {
661 self.name_type(property, value, &mut json);
662 }
663 (value, json, self.primitive_element(value))
664 })
665 .filter(|(_, json, element)| !is_empty(json) || element.is_some())
666 .collect();
667 if values.is_empty() {
668 continue;
669 }
670 let mut put = |key: String, json: Value, element: Option<Value>| {
671 if !json.is_null() {
672 object.insert(key.clone(), json);
673 }
674 if let Some(element) = element {
675 object.insert(format!("_{key}"), element);
676 }
677 };
678 if property.choice {
679 for (value, json, element) in values {
680 let suffix = self
681 .fhir_type(value)
682 .map(|t| capitalize(&t))
683 .unwrap_or_default();
684 put(format!("{}{suffix}", property.name), json, element);
685 }
686 } else if property.many {
687 let any_element = values.iter().any(|(_, _, e)| e.is_some());
688 let any_value = values.iter().any(|(_, json, _)| !json.is_null());
689 let (jsons, elements): (Vec<Value>, Vec<Value>) = values
690 .into_iter()
691 .map(|(_, json, element)| (json, element.unwrap_or(Value::Null)))
692 .unzip();
693 if any_value {
694 object.insert(property.name.clone(), Value::Array(jsons));
695 }
696 if any_element {
697 object.insert(format!("_{}", property.name), Value::Array(elements));
698 }
699 } else {
700 let (_, json, element) = values.into_iter().last().expect("not empty");
701 put(property.name.clone(), json, element);
702 }
703 }
704 }
705}
706
707fn element_order(ty: &Type, found: &fml_check::ElementRef, types: &Types<'_>) -> Option<usize> {
710 let index = found.element.index?;
711 let Type::Node { structure, path } = ty else {
712 return Some(index);
713 };
714 if !path.is_empty() {
715 return Some(index);
716 }
717 let mut depth = 0;
719 let mut current = Some(structure.clone());
720 while let Some(structure) = current {
721 if structure.url == found.owner.url || depth >= 32 {
722 break;
723 }
724 depth += 1;
725 current = structure
726 .base
727 .as_deref()
728 .and_then(|base| types.definitions.structure(base));
729 }
730 Some((64 - depth.min(63)) * 1_000_000 + index)
731}
732
733fn declared_key(ty: &Type) -> Option<String> {
735 match ty {
736 Type::Unknown => None,
737 known => Some(known.key()),
738 }
739}
740
741fn is_empty(json: &Value) -> bool {
743 match json {
744 Value::Null => true,
745 Value::Object(map) => map.is_empty(),
746 Value::Array(items) => items.is_empty(),
747 _ => false,
748 }
749}
750
751fn type_label(ty: &Type) -> Option<String> {
754 match ty {
755 Type::Node { structure, path } if path.is_empty() => {
756 Some(if structure.kind == fml_check::Kind::Logical {
757 structure.name.clone()
758 } else {
759 structure.type_name.clone()
760 })
761 }
762 _ => None,
763 }
764}
765
766pub(crate) fn capitalize(text: &str) -> String {
767 let mut chars = text.chars();
768 match chars.next() {
769 Some(first) => first.to_uppercase().chain(chars).collect(),
770 None => String::new(),
771 }
772}