use std::io;
use std::path::Path;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Project {
directory: &'static str,
contexts: &'static [&'static str],
files: &'static [File],
accounted: &'static [&'static str],
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("the {project} project compiles no context mapping named `{context}`")]
pub struct UnknownContext {
pub project: &'static str,
pub context: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct File {
pub name: &'static str,
pub text: &'static str,
}
impl Project {
#[must_use]
pub const fn directory(&self) -> &'static str {
self.directory
}
#[must_use]
pub const fn contexts(&self) -> &'static [&'static str] {
self.contexts
}
pub fn with_context(self, name: &str) -> Result<Self, UnknownContext> {
let contexts = self.contexts;
let chosen = contexts
.iter()
.find(|held| **held == name)
.ok_or_else(|| UnknownContext {
project: self.directory,
context: name.to_owned(),
})?;
Ok(Self {
contexts: std::slice::from_ref(chosen),
..self
})
}
#[must_use]
pub const fn files(&self) -> &'static [File] {
self.files
}
#[must_use]
pub const fn accounted(&self) -> &'static [&'static str] {
self.accounted
}
pub fn write_to(&self, directory: &Path) -> io::Result<Vec<PathBuf>> {
self.files
.iter()
.map(|file| {
let path = directory.join(file.name);
std::fs::write(&path, file.text).map(|()| path)
})
.collect()
}
}
#[cfg(any(
feature = "prescription",
feature = "dispensation",
feature = "imaging",
feature = "laboratory",
feature = "patient-summary",
feature = "discharge"
))]
macro_rules! shipped {
($directory:literal, $name:literal) => {
File {
name: $name,
text: include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/mappings/",
$directory,
"/",
$name
)),
}
};
}
#[cfg(any(feature = "prescription", feature = "dispensation"))]
pub const MPD: Project = Project {
directory: "mpd",
contexts: &[
"ferrobridge_eu_mpd_request.context",
"ferrobridge_eu_mpd_dispense.context",
],
files: &[
shipped!("mpd", "ferrobridge_eu_mpd_request.context.yml"),
shipped!("mpd", "ferrobridge_medication_order.v3.yml"),
shipped!("mpd", "ferrobridge_medication.v2.yml"),
shipped!("mpd", "ferrobridge_dosage.v2.yml"),
shipped!("mpd", "ferrobridge_eu_mpd_request.yml"),
shipped!("mpd", "ferrobridge_eu_mpd_medication.yml"),
shipped!("mpd", "ferrobridge_eu_mpd_dispense.context.yml"),
shipped!("mpd", "ferrobridge_medication.v1.yml"),
shipped!("mpd", "ferrobridge_eu_mpd_dispense.yml"),
],
accounted: &[],
};
#[cfg(feature = "imaging")]
pub const IMAGING: Project = Project {
directory: "imaging",
contexts: &["ferrobridge_eu_imaging.context"],
files: &[
shipped!("imaging", "ferrobridge_eu_imaging.context.yml"),
shipped!("imaging", "ferrobridge_imaging_exam_result.v1.yml"),
shipped!("imaging", "ferrobridge_eu_imaging_report.yml"),
],
accounted: &[
"Composition.section",
"DiagnosticReport.extension",
"DiagnosticReport.modifierExtension",
"DiagnosticReport.identifier",
"DiagnosticReport.basedOn",
"DiagnosticReport.status",
"DiagnosticReport.category",
"DiagnosticReport.code",
"DiagnosticReport.encounter",
"DiagnosticReport.issued",
"DiagnosticReport.performer",
"DiagnosticReport.resultsInterpreter",
"DiagnosticReport.specimen",
"DiagnosticReport.result",
"DiagnosticReport.media",
"DiagnosticReport.conclusion",
"DiagnosticReport.conclusionCode",
"DiagnosticReport.presentedForm",
"ImagingStudy.extension",
"ImagingStudy.modifierExtension",
"ImagingStudy.identifier",
"ImagingStudy.status",
"ImagingStudy.modality",
"ImagingStudy.encounter",
"ImagingStudy.started",
"ImagingStudy.basedOn",
"ImagingStudy.referrer",
"ImagingStudy.interpreter",
"ImagingStudy.endpoint",
"ImagingStudy.numberOfSeries",
"ImagingStudy.numberOfInstances",
"ImagingStudy.procedureReference",
"ImagingStudy.procedureCode",
"ImagingStudy.location",
"ImagingStudy.reasonCode",
"ImagingStudy.reasonReference",
"ImagingStudy.note",
"ImagingStudy.description",
"ImagingStudy.series",
],
};
#[cfg(feature = "laboratory")]
pub const LABORATORY: Project = Project {
directory: "laboratory",
contexts: &[
"ferrobridge_eu_laboratory.context",
"ferrobridge_eu_lab_composition.context",
],
files: &[
shipped!("laboratory", "ferrobridge_eu_laboratory.context.yml"),
shipped!("laboratory", "ferrobridge_laboratory_test_result.v1.yml"),
shipped!("laboratory", "ferrobridge_laboratory_test_analyte.v1.yml"),
shipped!("laboratory", "ferrobridge_specimen.v1.yml"),
shipped!("laboratory", "ferrobridge_eu_laboratory_result.yml"),
shipped!("laboratory", "ferrobridge_eu_laboratory_analyte.yml"),
shipped!("laboratory", "ferrobridge_eu_laboratory_specimen.yml"),
shipped!("laboratory", "ferrobridge_eu_lab_composition.context.yml"),
shipped!("laboratory", "ferrobridge_report_result.v1.Composition.yml"),
shipped!("laboratory", "ferrobridge_eu_lab_composition.yml"),
shipped!("laboratory", "ferrobridge_eu_lab_composition_report.yml"),
],
accounted: &[
"DiagnosticReport.basedOn.reference",
"DiagnosticReport.basedOn.identifier",
"DiagnosticReport.basedOn.display",
"DiagnosticReport.basedOn.extension",
"Observation.basedOn",
"Observation.referenceRange",
],
};
#[cfg(feature = "patient-summary")]
pub const EPS: Project = Project {
directory: "eps",
contexts: &["ferrobridge_eu_eps.context"],
files: &[
shipped!("eps", "ferrobridge_eu_eps.context.yml"),
shipped!("eps", "ferrobridge_health_summary.v1.Composition.yml"),
shipped!("eps", "ferrobridge_eu_eps_medication.context.yml"),
shipped!("eps", "ferrobridge_section_adhoc.v1.yml"),
shipped!("eps", "ferrobridge_eu_eps_medication_section.yml"),
shipped!(
"eps",
"ferrobridge_medication_order.v3.MedicationStatement.yml"
),
shipped!("eps", "ferrobridge_medication.v1.MedicationStatement.yml"),
shipped!("eps", "ferrobridge_eu_eps_immunization.context.yml"),
shipped!("eps", "ferrobridge_section_immunisation_list.v0.yml"),
shipped!("eps", "ferrobridge_eu_eps_immunization_section.yml"),
shipped!("eps", "ferrobridge_medication.v1.Immunization.yml"),
],
accounted: &[],
};
#[cfg(feature = "discharge")]
pub const HDR: Project = Project {
directory: "hdr",
contexts: &["ferrobridge_eu_hdr.context"],
files: &[
shipped!("hdr", "ferrobridge_eu_hdr.context.yml"),
shipped!("hdr", "ferrobridge_transfer_summary.v1.Composition.yml"),
shipped!("hdr", "ferrobridge_problem_diagnosis.v1.yml"),
shipped!("hdr", "ferrobridge_procedure.v1.yml"),
shipped!("hdr", "ferrobridge_medication_order.v3.yml"),
],
accounted: &[
"Composition.section",
"Condition.extension",
"Condition.modifierExtension",
"Condition.identifier",
"Condition.clinicalStatus",
"Condition.verificationStatus",
"Condition.category",
"Condition.severity",
"Condition.code",
"Condition.bodySite",
"Condition.recordedDate",
"Condition.recorder",
"Condition.asserter",
"Condition.stage",
"Condition.evidence",
"Condition.note",
"Procedure.extension",
"Procedure.modifierExtension",
"Procedure.identifier",
"Procedure.instantiatesCanonical",
"Procedure.instantiatesUri",
"Procedure.basedOn",
"Procedure.partOf",
"Procedure.status",
"Procedure.statusReason",
"Procedure.category",
"Procedure.code",
"Procedure.recorder",
"Procedure.asserter",
"Procedure.performer",
"Procedure.location",
"Procedure.reasonCode",
"Procedure.reasonReference",
"Procedure.bodySite",
"Procedure.outcome",
"Procedure.report",
"Procedure.complication",
"Procedure.complicationDetail",
"Procedure.followUp",
"Procedure.note",
"Procedure.focalDevice",
"Procedure.usedReference",
"Procedure.usedCode",
"MedicationRequest.extension",
"MedicationRequest.modifierExtension",
"MedicationRequest.identifier",
"MedicationRequest.status",
"MedicationRequest.statusReason",
"MedicationRequest.intent",
"MedicationRequest.category",
"MedicationRequest.priority",
"MedicationRequest.doNotPerform",
"MedicationRequest.supportingInformation",
"MedicationRequest.authoredOn",
"MedicationRequest.requester",
"MedicationRequest.performer",
"MedicationRequest.performerType",
"MedicationRequest.recorder",
"MedicationRequest.reasonCode",
"MedicationRequest.reasonReference",
"MedicationRequest.instantiatesCanonical",
"MedicationRequest.instantiatesUri",
"MedicationRequest.basedOn",
"MedicationRequest.groupIdentifier",
"MedicationRequest.courseOfTherapyType",
"MedicationRequest.insurance",
"MedicationRequest.note",
"MedicationRequest.dosageInstruction",
"MedicationRequest.dispenseRequest",
"MedicationRequest.substitution",
"MedicationRequest.priorPrescription",
"MedicationRequest.detectedIssue",
"MedicationRequest.eventHistory",
],
};
const SHIPPED: &[Project] = &[
#[cfg(any(feature = "prescription", feature = "dispensation"))]
MPD,
#[cfg(feature = "imaging")]
IMAGING,
#[cfg(feature = "laboratory")]
LABORATORY,
#[cfg(feature = "patient-summary")]
EPS,
#[cfg(feature = "discharge")]
HDR,
];
pub(crate) fn accounted_for(contexts: &[&str]) -> &'static [&'static str] {
SHIPPED
.iter()
.find(|project| project.contexts.iter().any(|held| contexts.contains(held)))
.map_or(&[], |project| project.accounted)
}