ferrobridge_validator/validate/
mod.rs1mod codes;
18mod extension;
19mod listing;
20mod matching;
21mod primitive;
22mod profiles;
23mod resolver;
24mod slicing;
25mod structure;
26mod walk;
27
28use std::collections::BTreeMap;
29use std::sync::Arc;
30use std::sync::Mutex;
31use std::sync::PoisonError;
32
33use fhir_types::codec::EncodeError;
34use fhir_types::codec::Value;
35
36use crate::report::Report;
37use crate::set::PackageId;
38use crate::set::PackageSet;
39use crate::set::ResolveError;
40use codes::Enumeration;
41use primitive::PrimitiveRule;
42use resolver::Contained;
43use resolver::Resolver;
44use structure::Structure;
45
46const CORE: &str = "http://hl7.org/fhir/StructureDefinition/";
48
49type Scoped = (String, Option<PackageId>);
51
52#[derive(Debug)]
54pub struct Validator<'set> {
55 set: &'set PackageSet,
56 structures: Mutex<BTreeMap<Scoped, Arc<Structure>>>,
57 primitives: Mutex<BTreeMap<String, Arc<PrimitiveRule>>>,
58 codes: Mutex<BTreeMap<Scoped, Arc<Enumeration>>>,
59 resolver: Box<dyn Resolver>,
60}
61
62#[derive(Debug, thiserror::Error)]
67pub enum ValidateError {
68 #[error(transparent)]
70 Resolve(#[from] ResolveError),
71 #[error("the StructureDefinition {url} carries no snapshot")]
73 NoSnapshot {
74 url: String,
76 },
77 #[error("the lexical form of {type_code} does not compile")]
79 Regex {
80 type_code: String,
82 #[source]
84 source: regex::Error,
85 },
86 #[error("the value of {element} in {structure} has no JSON form")]
88 Encode {
89 structure: String,
91 element: String,
93 #[source]
95 source: EncodeError,
96 },
97 #[error("the R4 element table carries no type {name}")]
99 UnknownType {
100 name: String,
102 },
103}
104
105impl<'set> Validator<'set> {
106 #[must_use]
108 pub fn new(set: &'set PackageSet) -> Self {
109 Self {
110 set,
111 structures: Mutex::new(BTreeMap::new()),
112 primitives: Mutex::new(BTreeMap::new()),
113 codes: Mutex::new(BTreeMap::new()),
114 resolver: Box::new(Contained),
115 }
116 }
117
118 pub fn validate(&self, resource: &Value) -> Result<Report, ValidateError> {
128 walk::Walk::new(self).run(resource, None)
129 }
130
131 pub fn validate_against(
140 &self,
141 resource: &Value,
142 profile: &str,
143 ) -> Result<Report, ValidateError> {
144 walk::Walk::new(self).run(resource, Some(profile))
145 }
146
147 fn structure(
151 &self,
152 canonical: &str,
153 from: Option<&Structure>,
154 ) -> Result<Arc<Structure>, ValidateError> {
155 let scope = from.map(Structure::origin);
156 let key = (canonical.to_owned(), scope);
157 if let Some(found) = self
158 .structures
159 .lock()
160 .unwrap_or_else(PoisonError::into_inner)
161 .get(&key)
162 {
163 return Ok(Arc::clone(found));
164 }
165 let (definition, origin) = self.set.structure_definition_in(canonical, scope)?;
166 let structure = Arc::new(Structure::new(definition, origin)?);
167 self.structures
168 .lock()
169 .unwrap_or_else(PoisonError::into_inner)
170 .insert(key, Arc::clone(&structure));
171 Ok(structure)
172 }
173
174 fn core(&self, name: &str) -> Result<Arc<Structure>, ValidateError> {
176 self.structure(&format!("{CORE}{name}"), None)
177 }
178
179 fn resolver(&self) -> &dyn Resolver {
181 self.resolver.as_ref()
182 }
183
184 fn codes(&self, canonical: &str, from: &Structure) -> Result<Arc<Enumeration>, ValidateError> {
187 let key = (canonical.to_owned(), Some(from.origin()));
188 if let Some(found) = self
189 .codes
190 .lock()
191 .unwrap_or_else(PoisonError::into_inner)
192 .get(&key)
193 {
194 return Ok(Arc::clone(found));
195 }
196 let enumeration = Arc::new(codes::enumerate(self.set, canonical, from.origin())?);
197 self.codes
198 .lock()
199 .unwrap_or_else(PoisonError::into_inner)
200 .insert(key, Arc::clone(&enumeration));
201 Ok(enumeration)
202 }
203
204 fn primitive(&self, code: &str) -> Result<Arc<PrimitiveRule>, ValidateError> {
206 if let Some(found) = self
207 .primitives
208 .lock()
209 .unwrap_or_else(PoisonError::into_inner)
210 .get(code)
211 {
212 return Ok(Arc::clone(found));
213 }
214 let rule = Arc::new(PrimitiveRule::read(&*self.core(code)?, code)?);
215 self.primitives
216 .lock()
217 .unwrap_or_else(PoisonError::into_inner)
218 .insert(code.to_owned(), Arc::clone(&rule));
219 Ok(rule)
220 }
221}