1use crate::fhir_types::{ElementDefinition, StructureDefinition};
7use rh_foundation::validation::{BindingStrength, ElementBinding};
8use std::collections::HashMap;
9
10pub fn extract_required_bindings(sd: &StructureDefinition) -> Vec<ElementBinding> {
24 let mut bindings = Vec::new();
25 let mut seen = HashMap::new();
26
27 if let Some(snapshot) = &sd.snapshot {
29 for element in &snapshot.element {
30 if let Some(binding) = extract_binding_from_element(element, &sd.base_type) {
31 if binding.strength == BindingStrength::Required {
33 if !seen.contains_key(&binding.path) {
35 seen.insert(binding.path.clone(), binding.clone());
36 bindings.push(binding);
37 }
38 }
39 }
40 }
41 }
42
43 bindings.sort_by(|a, b| a.path.cmp(&b.path));
45 bindings
46}
47
48fn extract_binding_from_element(
50 element: &ElementDefinition,
51 _resource_type: &str,
52) -> Option<ElementBinding> {
53 let binding = element.binding.as_ref()?;
54 let value_set_url = binding.value_set.as_ref()?;
55
56 let strength = BindingStrength::from_fhir_str(&binding.strength)?;
58
59 let path = element.path.clone();
61
62 let mut elem_binding = ElementBinding::new(path, strength, value_set_url);
64 if let Some(desc) = &binding.description {
65 elem_binding = elem_binding.with_description(desc);
66 }
67
68 Some(elem_binding)
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::fhir_types::{ElementBinding as FhirElementBinding, StructureDefinitionSnapshot};
75
76 fn make_test_element(path: &str, strength: &str, value_set: &str) -> ElementDefinition {
77 ElementDefinition {
78 id: Some(path.to_string()),
79 path: path.to_string(),
80 short: None,
81 definition: None,
82 min: None,
83 max: None,
84 element_type: None,
85 fixed: None,
86 pattern: None,
87 binding: Some(FhirElementBinding {
88 strength: strength.to_string(),
89 description: Some("Test binding".to_string()),
90 value_set: Some(value_set.to_string()),
91 }),
92 constraint: None,
93 }
94 }
95
96 fn make_test_sd(elements: Vec<ElementDefinition>) -> StructureDefinition {
97 StructureDefinition {
98 resource_type: "StructureDefinition".to_string(),
99 id: "test".to_string(),
100 url: "http://test.com/test".to_string(),
101 version: None,
102 name: "Test".to_string(),
103 title: None,
104 status: "active".to_string(),
105 description: None,
106 purpose: None,
107 kind: "resource".to_string(),
108 is_abstract: false,
109 base_type: "Patient".to_string(),
110 base_definition: None,
111 differential: None,
112 snapshot: Some(StructureDefinitionSnapshot { element: elements }),
113 }
114 }
115
116 #[test]
117 fn test_extract_required_binding() {
118 let element = make_test_element(
119 "Patient.gender",
120 "required",
121 "http://hl7.org/fhir/ValueSet/administrative-gender",
122 );
123 let sd = make_test_sd(vec![element]);
124
125 let bindings = extract_required_bindings(&sd);
126
127 assert_eq!(bindings.len(), 1);
128 assert_eq!(bindings[0].path, "Patient.gender");
129 assert_eq!(bindings[0].strength, BindingStrength::Required);
130 assert_eq!(
131 bindings[0].value_set_url,
132 "http://hl7.org/fhir/ValueSet/administrative-gender"
133 );
134 assert!(bindings[0].description.is_some());
135 }
136
137 #[test]
138 fn test_extract_multiple_required_bindings() {
139 let elements = vec![
140 make_test_element(
141 "Patient.gender",
142 "required",
143 "http://hl7.org/fhir/ValueSet/administrative-gender",
144 ),
145 make_test_element(
146 "Patient.contact.gender",
147 "required",
148 "http://hl7.org/fhir/ValueSet/administrative-gender",
149 ),
150 ];
151 let sd = make_test_sd(elements);
152
153 let bindings = extract_required_bindings(&sd);
154
155 assert_eq!(bindings.len(), 2);
156 assert_eq!(bindings[0].path, "Patient.contact.gender");
158 assert_eq!(bindings[1].path, "Patient.gender");
159 }
160
161 #[test]
162 fn test_skip_non_required_bindings() {
163 let elements = vec![
164 make_test_element(
165 "Patient.gender",
166 "required",
167 "http://hl7.org/fhir/ValueSet/administrative-gender",
168 ),
169 make_test_element(
170 "Patient.maritalStatus",
171 "extensible",
172 "http://hl7.org/fhir/ValueSet/marital-status",
173 ),
174 make_test_element(
175 "Patient.communication.language",
176 "preferred",
177 "http://hl7.org/fhir/ValueSet/languages",
178 ),
179 ];
180 let sd = make_test_sd(elements);
181
182 let bindings = extract_required_bindings(&sd);
183
184 assert_eq!(bindings.len(), 1);
186 assert_eq!(bindings[0].path, "Patient.gender");
187 }
188
189 #[test]
190 fn test_deduplicate_bindings() {
191 let element = make_test_element(
193 "Patient.gender",
194 "required",
195 "http://hl7.org/fhir/ValueSet/administrative-gender",
196 );
197 let sd = make_test_sd(vec![element.clone(), element]);
198
199 let bindings = extract_required_bindings(&sd);
200
201 assert_eq!(bindings.len(), 1);
203 }
204
205 #[test]
206 fn test_no_bindings() {
207 let element = ElementDefinition {
208 id: Some("Patient.id".to_string()),
209 path: "Patient.id".to_string(),
210 short: None,
211 definition: None,
212 min: None,
213 max: None,
214 element_type: None,
215 fixed: None,
216 pattern: None,
217 binding: None,
218 constraint: None,
219 };
220 let sd = make_test_sd(vec![element]);
221
222 let bindings = extract_required_bindings(&sd);
223
224 assert_eq!(bindings.len(), 0);
225 }
226}