1use std::sync::Arc;
8
9use crate::definitions::{Definitions, Element, Kind, Structure, same_primitive, type_url};
10
11#[derive(Debug, Clone)]
13pub enum Type {
14 Unknown,
16 Node {
17 structure: Arc<Structure>,
18 path: Vec<String>,
20 },
21}
22
23impl Type {
24 pub fn key(&self) -> String {
26 match self {
27 Type::Unknown => "?".to_owned(),
28 Type::Node { structure, path } => {
29 let mut key = structure.url.clone();
30 for segment in path {
31 key.push('.');
32 key.push_str(segment);
33 }
34 key
35 }
36 }
37 }
38
39 pub fn display(&self) -> String {
41 match self {
42 Type::Unknown => "?".to_owned(),
43 Type::Node { structure, path } => {
44 let mut name = structure.type_name.clone();
45 for segment in path {
46 name.push('.');
47 name.push_str(segment);
48 }
49 name
50 }
51 }
52 }
53
54 pub fn fhir_type(&self) -> Option<String> {
57 match self {
58 Type::Unknown => None,
59 Type::Node { structure, path } if path.is_empty() => Some(structure.type_name.clone()),
60 Type::Node { .. } => Some("BackboneElement".to_owned()),
61 }
62 }
63
64 pub fn structure(&self) -> Option<&Arc<Structure>> {
65 match self {
66 Type::Unknown => None,
67 Type::Node { structure, .. } => Some(structure),
68 }
69 }
70
71 pub fn is_resource(&self) -> bool {
73 matches!(self, Type::Node { structure, path } if path.is_empty() && structure.kind == Kind::Resource)
74 }
75
76 pub fn is_primitive(&self) -> bool {
77 matches!(self, Type::Node { structure, path } if path.is_empty() && structure.kind == Kind::PrimitiveType)
78 }
79}
80
81#[derive(Debug, Clone)]
83pub struct ElementRef {
84 pub name: String,
86 pub element: Element,
87 pub owner: Arc<Structure>,
89 pub path: Vec<String>,
91}
92
93#[derive(Debug, Clone)]
95pub enum Lookup {
96 Found(Box<ElementRef>),
97 Missing,
99 Unknown,
101}
102
103#[derive(Clone, Copy)]
105pub struct Types<'a> {
106 pub definitions: &'a dyn Definitions,
107}
108
109impl<'a> Types<'a> {
110 pub fn new(definitions: &'a dyn Definitions) -> Self {
111 Self { definitions }
112 }
113
114 pub fn structure(&self, url: &str) -> Type {
116 match self.definitions.structure(url) {
117 Some(structure) => Type::Node {
118 structure,
119 path: Vec::new(),
120 },
121 None => Type::Unknown,
122 }
123 }
124
125 pub fn lookup(&self, ty: &Type, name: &str) -> Lookup {
127 let Type::Node { structure, path } = ty else {
128 return Lookup::Unknown;
129 };
130 if path.is_empty() {
131 return self.lookup_in_structure(structure.clone(), name, 0);
132 }
133 let Some(node) = self.element_at(structure, path) else {
134 return Lookup::Unknown;
135 };
136 if let Some(element) = node.children.get(name) {
137 let mut element_path = path.clone();
138 element_path.push(name.to_owned());
139 return Lookup::Found(Box::new(ElementRef {
140 name: name.to_owned(),
141 element: element.clone(),
142 owner: structure.clone(),
143 path: element_path,
144 }));
145 }
146 match node.types.first() {
148 Some(code) => match self.definitions.structure(&type_url(code, &structure.url)) {
149 Some(base) => self.lookup_in_structure(base, name, 0),
150 None => Lookup::Unknown,
151 },
152 None => Lookup::Unknown,
153 }
154 }
155
156 pub fn elements(&self, ty: &Type) -> Vec<ElementRef> {
159 let Type::Node { structure, path } = ty else {
160 return Vec::new();
161 };
162 let mut found = Vec::new();
163 if path.is_empty() {
164 self.collect_elements(structure.clone(), &mut found);
165 return found;
166 }
167 let Some(node) = self.element_at(structure, path) else {
168 return found;
169 };
170 let mut children: Vec<(&String, &Element)> = node.children.iter().collect();
171 children.sort_by_key(|(_, element)| element.index);
172 for (name, element) in children {
173 let mut element_path = path.clone();
174 element_path.push(name.clone());
175 found.push(ElementRef {
176 name: name.clone(),
177 element: element.clone(),
178 owner: structure.clone(),
179 path: element_path,
180 });
181 }
182 if let Some(base) = node
183 .types
184 .first()
185 .and_then(|code| self.definitions.structure(&type_url(code, &structure.url)))
186 {
187 self.collect_elements(base, &mut found);
188 }
189 found
190 }
191
192 fn collect_elements(&self, structure: Arc<Structure>, found: &mut Vec<ElementRef>) {
193 let mut current = Some(structure);
194 let mut depth = 0;
195 while let Some(structure) = current.take() {
196 if structure.kind == Kind::PrimitiveType && !found.iter().any(|e| e.name == "value") {
197 found.push(ElementRef {
198 name: "value".to_owned(),
199 element: Element::default(),
200 owner: structure.clone(),
201 path: vec!["value".to_owned()],
202 });
203 }
204 let mut elements: Vec<(&String, &Element)> = structure.elements.iter().collect();
205 elements.sort_by_key(|(_, element)| element.index);
206 for (name, element) in elements {
207 if found.iter().any(|e| &e.name == name) {
208 continue;
209 }
210 found.push(ElementRef {
211 name: name.clone(),
212 element: element.clone(),
213 owner: structure.clone(),
214 path: vec![name.clone()],
215 });
216 }
217 depth += 1;
218 if depth > 16 {
219 break;
220 }
221 current = structure
222 .base
223 .as_deref()
224 .and_then(|base| self.definitions.structure(base));
225 }
226 }
227
228 fn lookup_in_structure(&self, structure: Arc<Structure>, name: &str, depth: usize) -> Lookup {
229 if structure.kind == Kind::PrimitiveType && name == "value" {
232 return Lookup::Found(Box::new(ElementRef {
233 name: name.to_owned(),
234 element: Element::default(),
235 owner: structure.clone(),
236 path: vec![name.to_owned()],
237 }));
238 }
239 if let Some(element) = structure.elements.get(name) {
240 return Lookup::Found(Box::new(ElementRef {
241 name: name.to_owned(),
242 element: element.clone(),
243 owner: structure.clone(),
244 path: vec![name.to_owned()],
245 }));
246 }
247 let Some(base) = &structure.base else {
248 return Lookup::Missing;
249 };
250 if depth > 16 {
251 return Lookup::Unknown;
252 }
253 match self.definitions.structure(base) {
254 Some(base) => self.lookup_in_structure(base, name, depth + 1),
255 None => Lookup::Unknown,
256 }
257 }
258
259 fn element_at(&self, structure: &Structure, path: &[String]) -> Option<Element> {
261 let (first, rest) = path.split_first()?;
262 let mut current = self.resolve_reference(structure.elements.get(first)?)?;
263 for segment in rest {
264 current = self.resolve_reference(current.children.get(segment)?)?;
265 }
266 Some(current)
267 }
268
269 fn resolve_reference(&self, element: &Element) -> Option<Element> {
271 match &element.content_reference {
272 Some((url, target)) => {
273 let referenced = self.definitions.structure(url)?;
274 self.element_at(&referenced, target)
275 }
276 None => Some(element.clone()),
277 }
278 }
279
280 pub fn element_type(&self, element: &ElementRef, chosen: Option<&str>) -> Type {
283 if let Some((url, target)) = &element.element.content_reference {
284 return match self.definitions.structure(url) {
285 Some(structure) => Type::Node {
286 structure,
287 path: target.clone(),
288 },
289 None => Type::Unknown,
290 };
291 }
292 if !element.element.children.is_empty() {
293 if let [code] = element.element.types.as_slice() {
297 let typed = self.structure(&type_url(code, &element.owner.url));
298 if matches!(&typed, Type::Node { structure, .. } if !structure.is_abstract)
299 && element
300 .element
301 .children
302 .keys()
303 .all(|name| matches!(self.lookup(&typed, name), Lookup::Found(_)))
304 {
305 return typed;
306 }
307 }
308 return Type::Node {
309 structure: element.owner.clone(),
310 path: element.path.clone(),
311 };
312 }
313 let code = match (chosen, element.element.types.as_slice()) {
314 (Some(chosen), _) => chosen.to_owned(),
315 (None, [single]) => single.clone(),
316 _ => return Type::Unknown,
317 };
318 self.structure(&type_url(&code, &element.owner.url))
319 }
320
321 pub fn named_type(&self, element: &ElementRef, name: &str) -> Type {
325 let candidates: Vec<String> = element
326 .element
327 .types
328 .iter()
329 .map(|code| type_url(code, &element.owner.url))
330 .collect();
331 for url in &candidates {
332 if let Some(structure) = self.definitions.structure(url)
333 && (structure.name == name
334 || structure.type_name == name
335 || url == name
336 || url.ends_with(&format!("/{name}")))
337 {
338 return Type::Node {
339 structure,
340 path: Vec::new(),
341 };
342 }
343 }
344 if name.contains("://") {
345 return self.structure(name);
346 }
347 for url in &candidates {
349 let Some((prefix, _)) = url.rsplit_once('/') else {
350 continue;
351 };
352 for id in [name.to_owned(), name.replace('_', "-")] {
353 if let Some(structure) = self.definitions.structure(&format!("{prefix}/{id}"))
354 && (structure.name == name || structure.type_name.ends_with(name))
355 {
356 return Type::Node {
357 structure,
358 path: Vec::new(),
359 };
360 }
361 }
362 }
363 Type::Unknown
364 }
365
366 pub fn is_one_of(&self, chosen: &str, types: &[String], context: &str) -> bool {
369 let matches = |name: &str| {
370 types
371 .iter()
372 .any(|t| same_primitive(t, name) || t == name || t.ends_with(&format!("/{name}")))
373 };
374 if matches(chosen) {
375 return true;
376 }
377 let mut url = type_url(chosen, context);
378 for _ in 0..16 {
379 let Some(structure) = self.definitions.structure(&url) else {
380 return !self.definitions.covers(&url);
382 };
383 if matches(&structure.type_name) {
384 return true;
385 }
386 match &structure.base {
387 Some(base) => url = base.clone(),
388 None => return false,
389 }
390 }
391 true
392 }
393}