Skip to main content

Crate openehr

Crate openehr 

Source
Expand description

openEHR Reference Model types, validation, paths, AQL parsing, and change-control security primitives — in Rust.

openEHR is a specification for clinical information: a small, stable Reference Model of about ninety classes, plus archetypes that constrain it into clinical content. This crate implements the Reference Model and the machinery around it, so that a Rust program can read, build, check, address, and safely disclose openEHR data without inventing its own idea of what a health record is.

use openehr::path::Pathable;
use openehr::rm::common::{Archetyped, LocatableAttrs, PartyIdentified};
use openehr::rm::data_structures::{Element, History, ItemTree, PointEvent};
use openehr::rm::data_types::{CodePhrase, DataValue, DvDateTime, DvQuantity};
use openehr::rm::ehr::{Composition, EntryAttrs, Observation};
use openehr::terminology::composition_category;
use openehr::validation::Validate;

let at = |name: &str, node: &str| LocatableAttrs::named(name, node).unwrap();
let quantity = |v: f64| DataValue::Quantity(DvQuantity::new(v, "mm[Hg]").unwrap());

let readings = ItemTree::new(at("blood pressure", "at0003"), vec![
    Element::new(at("Systolic", "at0004"), quantity(184.0)).into(),
    Element::new(at("Diastolic", "at0005"), quantity(96.0)).into(),
]);

let observation = Observation::new(
    at("Blood pressure", "openEHR-EHR-OBSERVATION.blood_pressure.v2")
        .with_archetype_details(
            Archetyped::new("openEHR-EHR-OBSERVATION.blood_pressure.v2", "1.1.0").unwrap(),
        ),
    EntryAttrs::about_subject(
        CodePhrase::new("ISO_639-1", "en").unwrap(),
        CodePhrase::new("IANA_character-sets", "UTF-8").unwrap(),
    ),
    History::new(
        at("Event Series", "at0001"),
        DvDateTime::new("2026-07-31T09:00:00Z").unwrap(),
        vec![PointEvent::new(
            at("any event", "at0006"),
            DvDateTime::new("2026-07-31T09:15:00Z").unwrap(),
            readings.into(),
        ).into()],
        None,
    ).unwrap(),
);

let composition = Composition::new(
    at("Encounter", "openEHR-EHR-COMPOSITION.encounter.v1")
        .with_archetype_details(
            Archetyped::new("openEHR-EHR-COMPOSITION.encounter.v1", "1.1.0").unwrap(),
        ),
    composition_category::EVENT,
    PartyIdentified::named("Dr A Nurse").unwrap().into(),
    CodePhrase::new("ISO_639-1", "en").unwrap(),
    CodePhrase::new("ISO_3166-1", "GB").unwrap(),
).unwrap().with_content(observation.into());

// It satisfies the Reference Model's invariants…
assert!(composition.validate().is_empty());

// …it is addressable by openEHR path…
let systolic = composition.item_at_path(
    "/content[openEHR-EHR-OBSERVATION.blood_pressure.v2]\
     /data/events[at0006]/data/items[at0004]/value/magnitude",
).unwrap();
assert_eq!(systolic.type_name(), "primitive");

// …and it round-trips through openEHR canonical JSON.
let json = serde_json::to_string(&composition).unwrap();
let back: Composition = serde_json::from_str(&json).unwrap();
assert_eq!(back, composition);

§What is here

ModuleopenEHR component
baseBASE: identifiers, references, intervals, ISO 8601
rm::data_typesRM: Data Types (DV_*)
rm::data_structuresRM: Data Structures (ITEM_*, CLUSTER, ELEMENT, HISTORY)
rm::commonRM: Common (archetyping, parties, audit, change control)
rm::ehrRM: EHR (COMPOSITION, entries, EHR_STATUS, FOLDER)
rm::demographicRM: Demographic (PERSON, ROLE, ORGANISATION)
terminologyTERM: the openEHR support terminology
pathopenEHR path parsing and navigation
aqlQUERY: AQL lexing, parsing, and static checking
validationRM invariant checking
securityEHR_ACCESS, audit chaining, redaction

§What is not here, and why

Saying this plainly is part of the design. A clinical library that implies coverage it does not have is worse than a small one.

Not implementedWhy
Archetypes and templates (AM, ADL, AOM2)a parser and a constraint engine, each larger than this crate
AQL executionneeds a repository; aql parses and checks, and returns no rows
Terminology lookup beyond openEHR’s ownneeds a terminology server; external codes are carried opaquely
UCUM unit conversiona wrong conversion is a thousand-fold dosing error
REST service, persistence, EHR Extractout of scope; see spec/01-scope.md
HL7 GTS / PIVL timing evaluationreturns Error::Unsupported rather than a guess

Where an openEHR operation is defined and not implemented, this crate returns Error::Unsupported naming the spec section that records the exclusion. It never returns a plausible default.

§Three design commitments

Refuse rather than guess. Comparison is partial throughout: a month-precision date is not ordered against a day inside that month (base::iso8601), 5 mg is not comparable with 5 mL (rm::data_types::quantity), and a path matching three elements fails rather than returning the first (path). Each of those has a plausible wrong answer that no downstream reader could detect.

Absence is structured. openEHR’s four null flavours — nobody looked, somebody looked and could not find out, the value is withheld, the question does not arise — are four different clinical facts, and this crate will not let them collapse. See rm::data_structures.

Nothing prints protected health information. No Display implementation renders an identifier or a media blob; no error echoes a submitted value (error); redaction masks rather than deletes, and counts rather than names what it withheld (security::redact).

§Two gates, not one

Constructors enforce invariants on data this program builds. validation enforces them on data this program receives — serde writes fields directly and never calls a constructor. A service that deserializes and stores without validating has no invariant checking at all.

§Specification

This crate is developed specification-first. Every normative statement has a stable identifier and lives in spec/, which also records the known gaps (spec/audit.md) and what is verified per requirement (spec/conformance-matrix.md). Requirement ids are cited inline in the code and in these docs — S1.4, Q12.9, X11.7 — so prose can be traced back to a decision.

Re-exports§

pub use error::Error;
pub use error::ParseError;
pub use error::PathError;
pub use error::Result;
pub use error::ValidationReport;
pub use error::Violation;

Modules§

aql
Archetype Query Language: lexing, parsing, and static checking.
base
The openEHR BASE component: identifiers, references, intervals, and the ISO 8601 primitives everything else is built from.
error
Errors.
path
openEHR paths: parsing, and navigation over a composition.
rm
The openEHR Reference Model.
security
Security: access control, tamper evidence, and withholding content.
terminology
The openEHR support terminology.
validation
Reference Model invariant checking.