1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! openEHR Reference Model types, validation, paths, AQL parsing, and
//! change-control security primitives — in Rust.
//!
//! [openEHR](https://specifications.openehr.org/) 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.
pub use ;