use fhir_types::codec::Json as _;
use fhir_types::codec::Object;
use fhir_types::r4::dosage::Dosage;
use fhir_types::r4::extension::Extension;
use fhir_types::r4::extension::ExtensionValue;
use fhir_types::r4::identifier::Identifier;
use fhir_types::r4::medication_dispense::MedicationDispense;
use fhir_types::r4::medication_dispense::MedicationDispenseMedication;
use fhir_types::r4::medication_dispense::MedicationDispensePerformer;
use fhir_types::r4::medication_dispense::MedicationDispenseSubstitution;
use fhir_types::r4::quantity::Quantity;
use fhir_types::r4::reference::Reference;
use crate::crosswalk::dispensation::MPD_MEDICATION_DISPENSE;
use crate::medication::Medicinal;
use crate::medication::Mpd;
use crate::medication::ReadError;
use crate::medication::WriteError;
use crate::medication::Written;
use crate::medication::annotation;
use crate::medication::claiming;
use crate::medication::decode;
use crate::medication::extension;
use crate::medication::note;
use crate::medication::single;
use crate::receive::conform::CheckError;
use crate::receive::conform::Conformance;
pub const RECORDED: &str =
"http://hl7.org/fhir/5.0/StructureDefinition/extension-MedicationDispense.recorded";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Dispense {
pub subject: Reference,
pub identifier: Vec<Identifier>,
pub author: Vec<Reference>,
pub date: String,
pub status: String,
pub language: Option<String>,
pub location: Option<Reference>,
pub receiver: Option<Reference>,
pub related_request: Vec<Reference>,
pub medication: Medicinal,
pub quantity: Quantity,
pub time_of_dispensation: Option<String>,
pub substitution_occurred: Option<bool>,
pub dosage: Vec<Dosage>,
pub note: Option<String>,
}
impl Dispense {
pub fn read(text: &str) -> Result<Self, ReadError> {
let dispense = decode::<MedicationDispense>(text, "MedicationDispense")?;
let missing = |element| ReadError::Missing { element };
let (coded, reference) = match &dispense.medication {
MedicationDispenseMedication::CodeableConcept(coded) => (Some(&**coded), None),
MedicationDispenseMedication::Reference(reference) => (None, Some(&**reference)),
};
let medication = Medicinal::read(
coded,
reference,
&dispense.contained,
"MedicationDispense.medication[x]",
)?;
if dispense.performer.is_empty() {
return Err(missing("MedicationDispense.performer"));
}
let date = extension(&dispense.extension, RECORDED, |value| match value {
ExtensionValue::DateTime(date) => date.value.clone(),
_ => None,
})?
.ok_or(missing("MedicationDispense.extension:recorded"))?;
let substitution_occurred = dispense
.substitution
.as_ref()
.map(|substitution| {
substitution
.was_substituted
.value
.ok_or(missing("MedicationDispense.substitution.wasSubstituted"))
})
.transpose()?;
Ok(Self {
subject: dispense
.subject
.clone()
.ok_or(missing("MedicationDispense.subject"))?,
identifier: dispense.identifier.clone(),
author: dispense
.performer
.iter()
.map(|performer| performer.actor.clone())
.collect(),
date,
status: dispense
.status
.value
.clone()
.ok_or(missing("MedicationDispense.status"))?,
language: dispense
.language
.as_ref()
.and_then(|code| code.value.clone()),
location: dispense.location.clone(),
receiver: single(&dispense.receiver, "MedicationDispense.receiver")?.cloned(),
related_request: dispense.authorizing_prescription.clone(),
medication,
quantity: dispense
.quantity
.clone()
.ok_or(missing("MedicationDispense.quantity"))?,
time_of_dispensation: dispense
.when_handed_over
.as_ref()
.and_then(|date| date.value.clone()),
substitution_occurred,
dosage: dispense.dosage_instruction.clone(),
note: note(&dispense.note, "MedicationDispense.note")?,
})
}
pub fn resource(&self) -> Result<MedicationDispense, WriteError> {
if self.author.is_empty() {
return Err(WriteError::Empty {
element: "EHDSMedicationDispense.header.author[x]",
});
}
let (medication, contained) = match self.medication.write() {
(Written::Coded(coded), contained) => (
MedicationDispenseMedication::CodeableConcept(coded),
contained,
),
(Written::Referenced(reference), contained) => (
MedicationDispenseMedication::Reference(reference),
contained,
),
};
Ok(MedicationDispense {
id: None,
meta: Some(claiming(MPD_MEDICATION_DISPENSE)),
implicit_rules: None,
language: self.language.as_deref().map(Into::into),
text: None,
contained: contained.into_iter().collect(),
extension: vec![Extension {
url: RECORDED.to_owned(),
value: Some(ExtensionValue::DateTime(self.date.as_str().into())),
..Extension::default()
}],
modifier_extension: Vec::new(),
identifier: self.identifier.clone(),
part_of: Vec::new(),
status: self.status.as_str().into(),
status_reason: None,
category: None,
medication,
subject: Some(self.subject.clone()),
context: None,
supporting_information: Vec::new(),
performer: self
.author
.iter()
.map(|actor| MedicationDispensePerformer {
actor: actor.clone(),
..MedicationDispensePerformer::default()
})
.collect(),
location: self.location.clone(),
authorizing_prescription: self.related_request.clone(),
r#type: None,
quantity: Some(self.quantity.clone()),
days_supply: None,
when_prepared: None,
when_handed_over: self.time_of_dispensation.as_deref().map(Into::into),
destination: None,
receiver: self.receiver.clone().into_iter().collect(),
note: annotation(self.note.as_ref()),
dosage_instruction: self.dosage.clone(),
substitution: self.substitution_occurred.map(|substituted| {
MedicationDispenseSubstitution {
was_substituted: substituted.into(),
..MedicationDispenseSubstitution::default()
}
}),
detected_issue: Vec::new(),
event_history: Vec::new(),
})
}
pub fn to_json(&self) -> Result<Object, WriteError> {
self.resource()?
.to_json()
.map_err(|source| WriteError::Encode { source })
}
}
impl Mpd {
pub fn check_dispense(&self, dispense: &Object) -> Result<Conformance, CheckError> {
self.check(&self.dispense, "MedicationDispense", dispense)
}
}