From 32144defa3beda0490ab74d40dc684c2ccba713d Mon Sep 17 00:00:00 2001 From: Flix Date: Sat, 25 Mar 2023 18:29:47 +0100 Subject: [PATCH] feat: Implement IdentifiableResource --- README.md | 7 +- crates/fhir-model/src/r4b/resources/mod.rs | 194 +++++++++++++++++++++ crates/fhir-model/tests/test_resources.rs | 22 ++- 3 files changed, 219 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c9dfce65..3382b830 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,13 @@ This is a [FHIR](https://www.hl7.org/fhir/) library in its early stages. The mod - [x] Linked code-fields to respective enums - [x] Builders for types and resources - [x] Allow to convert code enums to Coding and CodeableConcept -- [ ] Implementation of base traits +- [x] Implementation of base traits - [x] (Base)Resource - [x] NamedResource - [x] DomainResource - - [ ] IdentifiableResource -- [ ] FHIR client implmentation + - [x] IdentifiableResource +- [ ] Extension traits (e.g. more convenience on identifier access) +- [ ] FHIR client implementation - [ ] FHIRpath implementation - [ ] Resource validation using FHIRpath - [ ] Search parameters diff --git a/crates/fhir-model/src/r4b/resources/mod.rs b/crates/fhir-model/src/r4b/resources/mod.rs index 7a0214b1..7e194fb3 100644 --- a/crates/fhir-model/src/r4b/resources/mod.rs +++ b/crates/fhir-model/src/r4b/resources/mod.rs @@ -4,3 +4,197 @@ mod generated; pub use generated::*; + +use super::types::{FieldExtension, Identifier}; + +/// Trait for all resources with multiple identifiers in the `identifier` field. +/// Simplifies access to identifiers. +pub trait IdentifiableResource { + /// Get the identifier field. + fn identifier(&self) -> &Vec>; + /// Get the identifier field mutably. + fn identifier_mut(&mut self) -> &mut Vec>; + /// Set the identifier field. + fn set_identifier(&mut self, value: Vec>); + + /// Get the identifier extension field. + fn identifier_ext(&self) -> &Vec>; + /// Get the identifier extension field mutably. + fn identifier_ext_mut(&mut self) -> &mut Vec>; + /// Set the identifier extension field. + fn set_identifier_ext(&mut self, value: Vec>); +} + +/// Implement the IdentifiableResource trait for the resources and the resource +/// enum. +macro_rules! impl_identifiable_resource { + ([$($resource:ident),*$(,)?]) => { + $(impl_identifiable_resource!($resource);)* + + impl Resource { + /// Return the resource as identifiable resource. + #[must_use] + pub fn as_identifiable_resource(&self) -> Option<&dyn IdentifiableResource> { + match self { + $( + Self::$resource(r) => Some(r), + )* + _ => None, + } + } + + /// Return the resource as mutable identifiable resource. + #[must_use] + pub fn as_identifiable_resource_mut(&mut self) -> Option<&mut dyn IdentifiableResource> { + match self { + $( + Self::$resource(r) => Some(r), + )* + _ => None, + } + } + } + }; + ($resource:ident) => { + impl IdentifiableResource for $resource { + fn identifier(&self) -> &Vec> { + &self.identifier + } + + fn identifier_mut(&mut self) -> &mut Vec> { + &mut self.identifier + } + + fn set_identifier(&mut self, value: Vec>) { + self.identifier = value; + } + + fn identifier_ext(&self) -> &Vec> { + &self.identifier_ext + } + + fn identifier_ext_mut(&mut self) -> &mut Vec> { + &mut self.identifier_ext + } + + fn set_identifier_ext(&mut self, value: Vec>) { + self.identifier_ext = value; + } + } + }; +} + +impl_identifiable_resource!([ + Account, + ActivityDefinition, + AdministrableProductDefinition, + AllergyIntolerance, + Appointment, + AppointmentResponse, + Basic, + BiologicallyDerivedProduct, + BodyStructure, + CarePlan, + CareTeam, + CatalogEntry, + ChargeItem, + ChargeItemDefinition, + Citation, + Claim, + ClaimResponse, + ClinicalImpression, + ClinicalUseDefinition, + CodeSystem, + Communication, + CommunicationRequest, + Condition, + Consent, + Contract, + Coverage, + CoverageEligibilityRequest, + CoverageEligibilityResponse, + DetectedIssue, + Device, + DeviceDefinition, + DeviceMetric, + DeviceRequest, + DeviceUseStatement, + DiagnosticReport, + DocumentManifest, + DocumentReference, + Encounter, + Endpoint, + EnrollmentRequest, + EnrollmentResponse, + EpisodeOfCare, + EventDefinition, + Evidence, + EvidenceReport, + EvidenceVariable, + ExampleScenario, + ExplanationOfBenefit, + FamilyMemberHistory, + Flag, + Goal, + Group, + GuidanceResponse, + HealthcareService, + ImagingStudy, + Immunization, + ImmunizationEvaluation, + ImmunizationRecommendation, + InsurancePlan, + Invoice, + Library, + List, + Location, + ManufacturedItemDefinition, + Measure, + MeasureReport, + Media, + Medication, + MedicationAdministration, + MedicationDispense, + MedicationRequest, + MedicationStatement, + MedicinalProductDefinition, + MessageDefinition, + MolecularSequence, + NutritionOrder, + Observation, + ObservationDefinition, + Organization, + OrganizationAffiliation, + PackagedProductDefinition, + Patient, + PaymentNotice, + PaymentReconciliation, + Person, + PlanDefinition, + Practitioner, + PractitionerRole, + Procedure, + Questionnaire, + RegulatedAuthorization, + RelatedPerson, + RequestGroup, + ResearchDefinition, + ResearchElementDefinition, + ResearchStudy, + ResearchSubject, + RiskAssessment, + Schedule, + ServiceRequest, + Slot, + Specimen, + StructureDefinition, + StructureMap, + SubscriptionTopic, + Substance, + SubstanceDefinition, + SupplyDelivery, + SupplyRequest, + Task, + ValueSet, + VisionPrescription +]); diff --git a/crates/fhir-model/tests/test_resources.rs b/crates/fhir-model/tests/test_resources.rs index d8674acb..f2b63c8a 100644 --- a/crates/fhir-model/tests/test_resources.rs +++ b/crates/fhir-model/tests/test_resources.rs @@ -9,7 +9,7 @@ use fhir_model::r4b::{ Basic, NamedResource, Patient, RequestGroup, RequestGroupAction, RequestGroupActionTiming, Resource, WrongResourceType, }, - types::{CodeableConcept, Coding}, + types::{CodeableConcept, Coding, Identifier}, }; use serde_json::Value; @@ -91,3 +91,23 @@ fn resource_traits() { patient.as_base_resource_mut().set_id(None); assert!(patient.as_base_resource().id().is_none()); } + +#[test] +fn identifiable_resource() { + let patient: Resource = Patient::builder() + .identifier(vec![Some( + Identifier::builder().system("system".to_owned()).value("bla".to_owned()).build(), + )]) + .build() + .into(); + assert!(patient.as_identifiable_resource().is_some()); + + let identifier = patient + .as_identifiable_resource() + .expect("Patient has identifiers") + .identifier() + .first() + .and_then(Option::as_ref) + .expect("We set one identifier"); + assert_eq!(identifier.system.as_deref(), Some("system")); +}