mod codes;
mod extension;
mod listing;
mod matching;
mod primitive;
mod profiles;
mod resolver;
mod slicing;
mod structure;
mod walk;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::PoisonError;
use fhir_types::codec::EncodeError;
use fhir_types::codec::Value;
use crate::report::Report;
use crate::set::PackageId;
use crate::set::PackageSet;
use crate::set::ResolveError;
use codes::Enumeration;
use primitive::PrimitiveRule;
use resolver::Contained;
use resolver::Resolver;
use structure::Structure;
const CORE: &str = "http://hl7.org/fhir/StructureDefinition/";
type Scoped = (String, Option<PackageId>);
#[derive(Debug)]
pub struct Validator<'set> {
set: &'set PackageSet,
structures: Mutex<BTreeMap<Scoped, Arc<Structure>>>,
primitives: Mutex<BTreeMap<String, Arc<PrimitiveRule>>>,
codes: Mutex<BTreeMap<Scoped, Arc<Enumeration>>>,
resolver: Box<dyn Resolver>,
}
#[derive(Debug, thiserror::Error)]
pub enum ValidateError {
#[error(transparent)]
Resolve(#[from] ResolveError),
#[error("the StructureDefinition {url} carries no snapshot")]
NoSnapshot {
url: String,
},
#[error("the lexical form of {type_code} does not compile")]
Regex {
type_code: String,
#[source]
source: regex::Error,
},
#[error("the value of {element} in {structure} has no JSON form")]
Encode {
structure: String,
element: String,
#[source]
source: EncodeError,
},
#[error("the R4 element table carries no type {name}")]
UnknownType {
name: String,
},
}
impl<'set> Validator<'set> {
#[must_use]
pub fn new(set: &'set PackageSet) -> Self {
Self {
set,
structures: Mutex::new(BTreeMap::new()),
primitives: Mutex::new(BTreeMap::new()),
codes: Mutex::new(BTreeMap::new()),
resolver: Box::new(Contained),
}
}
pub fn validate(&self, resource: &Value) -> Result<Report, ValidateError> {
walk::Walk::new(self).run(resource, None)
}
pub fn validate_against(
&self,
resource: &Value,
profile: &str,
) -> Result<Report, ValidateError> {
walk::Walk::new(self).run(resource, Some(profile))
}
fn structure(
&self,
canonical: &str,
from: Option<&Structure>,
) -> Result<Arc<Structure>, ValidateError> {
let scope = from.map(Structure::origin);
let key = (canonical.to_owned(), scope);
if let Some(found) = self
.structures
.lock()
.unwrap_or_else(PoisonError::into_inner)
.get(&key)
{
return Ok(Arc::clone(found));
}
let (definition, origin) = self.set.structure_definition_in(canonical, scope)?;
let structure = Arc::new(Structure::new(definition, origin)?);
self.structures
.lock()
.unwrap_or_else(PoisonError::into_inner)
.insert(key, Arc::clone(&structure));
Ok(structure)
}
fn core(&self, name: &str) -> Result<Arc<Structure>, ValidateError> {
self.structure(&format!("{CORE}{name}"), None)
}
fn resolver(&self) -> &dyn Resolver {
self.resolver.as_ref()
}
fn codes(&self, canonical: &str, from: &Structure) -> Result<Arc<Enumeration>, ValidateError> {
let key = (canonical.to_owned(), Some(from.origin()));
if let Some(found) = self
.codes
.lock()
.unwrap_or_else(PoisonError::into_inner)
.get(&key)
{
return Ok(Arc::clone(found));
}
let enumeration = Arc::new(codes::enumerate(self.set, canonical, from.origin())?);
self.codes
.lock()
.unwrap_or_else(PoisonError::into_inner)
.insert(key, Arc::clone(&enumeration));
Ok(enumeration)
}
fn primitive(&self, code: &str) -> Result<Arc<PrimitiveRule>, ValidateError> {
if let Some(found) = self
.primitives
.lock()
.unwrap_or_else(PoisonError::into_inner)
.get(code)
{
return Ok(Arc::clone(found));
}
let rule = Arc::new(PrimitiveRule::read(&*self.core(code)?, code)?);
self.primitives
.lock()
.unwrap_or_else(PoisonError::into_inner)
.insert(code.to_owned(), Arc::clone(&rule));
Ok(rule)
}
}