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 fn lookup_in_structure(&self, structure: Arc<Structure>, name: &str, depth: usize) -> Lookup {
157 if structure.kind == Kind::PrimitiveType && name == "value" {
160 return Lookup::Found(Box::new(ElementRef {
161 name: name.to_owned(),
162 element: Element::default(),
163 owner: structure.clone(),
164 path: vec![name.to_owned()],
165 }));
166 }
167 if let Some(element) = structure.elements.get(name) {
168 return Lookup::Found(Box::new(ElementRef {
169 name: name.to_owned(),
170 element: element.clone(),
171 owner: structure.clone(),
172 path: vec![name.to_owned()],
173 }));
174 }
175 let Some(base) = &structure.base else {
176 return Lookup::Missing;
177 };
178 if depth > 16 {
179 return Lookup::Unknown;
180 }
181 match self.definitions.structure(base) {
182 Some(base) => self.lookup_in_structure(base, name, depth + 1),
183 None => Lookup::Unknown,
184 }
185 }
186
187 fn element_at(&self, structure: &Structure, path: &[String]) -> Option<Element> {
189 let (first, rest) = path.split_first()?;
190 let mut current = self.resolve_reference(structure.elements.get(first)?)?;
191 for segment in rest {
192 current = self.resolve_reference(current.children.get(segment)?)?;
193 }
194 Some(current)
195 }
196
197 fn resolve_reference(&self, element: &Element) -> Option<Element> {
199 match &element.content_reference {
200 Some((url, target)) => {
201 let referenced = self.definitions.structure(url)?;
202 self.element_at(&referenced, target)
203 }
204 None => Some(element.clone()),
205 }
206 }
207
208 pub fn element_type(&self, element: &ElementRef, chosen: Option<&str>) -> Type {
211 if let Some((url, target)) = &element.element.content_reference {
212 return match self.definitions.structure(url) {
213 Some(structure) => Type::Node {
214 structure,
215 path: target.clone(),
216 },
217 None => Type::Unknown,
218 };
219 }
220 if !element.element.children.is_empty() {
221 return Type::Node {
222 structure: element.owner.clone(),
223 path: element.path.clone(),
224 };
225 }
226 let code = match (chosen, element.element.types.as_slice()) {
227 (Some(chosen), _) => chosen.to_owned(),
228 (None, [single]) => single.clone(),
229 _ => return Type::Unknown,
230 };
231 self.structure(&type_url(&code, &element.owner.url))
232 }
233
234 pub fn is_one_of(&self, chosen: &str, types: &[String], context: &str) -> bool {
237 let matches = |name: &str| {
238 types
239 .iter()
240 .any(|t| same_primitive(t, name) || t == name || t.ends_with(&format!("/{name}")))
241 };
242 if matches(chosen) {
243 return true;
244 }
245 let mut url = type_url(chosen, context);
246 for _ in 0..16 {
247 let Some(structure) = self.definitions.structure(&url) else {
248 return !self.definitions.covers(&url);
250 };
251 if matches(&structure.type_name) {
252 return true;
253 }
254 match &structure.base {
255 Some(base) => url = base.clone(),
256 None => return false,
257 }
258 }
259 true
260 }
261}