rh_codegen/generators/
cardinality_generator.rs1use crate::fhir_types::StructureDefinition;
7
8pub struct CardinalityGenerator;
10
11impl CardinalityGenerator {
12 pub fn generate_cardinalities_constant(structure_def: &StructureDefinition) -> String {
27 let cardinalities = Self::extract_cardinalities(structure_def);
28
29 let mut code = String::new();
30 code.push_str("/// FHIR cardinality constraints for this resource/datatype\n");
31 code.push_str("///\n");
32 code.push_str(
33 "/// These define the minimum and maximum occurrences allowed for each element.\n",
34 );
35 if cardinalities.is_empty() {
36 code.push_str("pub static CARDINALITIES: once_cell::sync::Lazy<Vec<rh_foundation::ElementCardinality>> = once_cell::sync::Lazy::new(Vec::new);");
37 code.push('\n');
38 return code;
39 }
40
41 code.push_str("pub static CARDINALITIES: once_cell::sync::Lazy<Vec<rh_foundation::ElementCardinality>> = once_cell::sync::Lazy::new(|| vec![\n");
42
43 for card in &cardinalities {
44 let path = escape_rust_string(&card.path);
45 let max_str = match card.max {
46 None => "None".to_string(),
47 Some(n) => format!("Some({n})"),
48 };
49
50 code.push_str(&format!(
51 " rh_foundation::ElementCardinality::new(\"{path}\", {}, {max_str}),\n",
52 card.min
53 ));
54 }
55
56 code.push_str("]);\n");
57 code
58 }
59
60 fn extract_cardinalities(
64 structure_def: &StructureDefinition,
65 ) -> Vec<rh_foundation::validation::ElementCardinality> {
66 let mut cardinalities = Vec::new();
67
68 let base_type = structure_def.name.as_str();
70
71 if let Some(snapshot) = &structure_def.snapshot {
73 for element in &snapshot.element {
74 if element.path == base_type {
76 continue;
77 }
78
79 if element.path.matches('.').count() > 1 {
82 }
86
87 let min = element.min.unwrap_or(0) as usize;
88 let max = if let Some(max_str) = &element.max {
89 if max_str == "*" {
90 None } else {
92 max_str.parse::<usize>().ok()
93 }
94 } else {
95 Some(1) };
97
98 cardinalities.push(rh_foundation::validation::ElementCardinality::new(
99 element.path.clone(),
100 min,
101 max,
102 ));
103 }
104 }
105
106 cardinalities
107 }
108}
109
110fn escape_rust_string(s: &str) -> String {
112 s.replace('\\', "\\\\")
113 .replace('"', "\\\"")
114 .replace('\n', "\\n")
115 .replace('\r', "\\r")
116 .replace('\t', "\\t")
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_escape_rust_string() {
125 assert_eq!(escape_rust_string("hello"), "hello");
126 assert_eq!(escape_rust_string("hello \"world\""), "hello \\\"world\\\"");
127 assert_eq!(escape_rust_string("path\\to\\file"), "path\\\\to\\\\file");
128 }
129}