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
| Module | openEHR component |
|---|---|
base | BASE: identifiers, references, intervals, ISO 8601 |
rm::data_types | RM: Data Types (DV_*) |
rm::data_structures | RM: Data Structures (ITEM_*, CLUSTER, ELEMENT, HISTORY) |
rm::common | RM: Common (archetyping, parties, audit, change control) |
rm::ehr | RM: EHR (COMPOSITION, entries, EHR_STATUS, FOLDER) |
rm::demographic | RM: Demographic (PERSON, ROLE, ORGANISATION) |
terminology | TERM: the openEHR support terminology |
path | openEHR path parsing and navigation |
aql | QUERY: AQL lexing, parsing, and static checking |
validation | RM invariant checking |
security | EHR_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 implemented | Why |
|---|---|
| Archetypes and templates (AM, ADL, AOM2) | a parser and a constraint engine, each larger than this crate |
| AQL execution | needs a repository; aql parses and checks, and returns no rows |
| Terminology lookup beyond openEHR’s own | needs a terminology server; external codes are carried opaquely |
| UCUM unit conversion | a wrong conversion is a thousand-fold dosing error |
| REST service, persistence, EHR Extract | out of scope; see spec/01-scope.md |
HL7 GTS / PIVL timing evaluation | returns 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.