Skip to main content

ehds_logging/
record.rs

1// SPDX-FileCopyrightText: Cadasto B.V.
2// SPDX-License-Identifier: BUSL-1.1
3
4//! The access record: what the logging component records "on every access
5//! event or group of events" (Regulation (EU) 2025/327 Annex II 3.2).
6//!
7//! Annex II 3.2 asks for (a) the healthcare provider or other individuals
8//! that accessed the data, (b) the specific natural person or persons, (c)
9//! the categories of data accessed, (d) the time and date, and (e) the
10//! origin or origins of the data. An [`AccessRecord`] carries each, and
11//! beside them the data subject whose data were accessed, the purpose of
12//! use the access was made for, which Art 9(2) asks the patient be told, and
13//! the policies it was authorised under, such as a data permit (Art 68). A
14//! record whose accessor declared an emergency purpose carries the mark
15//! Art 11(5) asks for ([`emergency`](crate::emergency)).
16//!
17//! A record names the patient and the person who accessed their data, so
18//! `Debug` shows neither, nor any id the classification read: a record
19//! leaves through an [`AccessSink`](crate::sink::AccessSink) alone.
20
21use std::collections::BTreeSet;
22use std::fmt;
23use std::net::IpAddr;
24
25use jiff::Timestamp;
26use secrecy::SecretString;
27
28use crate::classify::Classification;
29use crate::emergency::Emergency;
30use crate::label::Labels;
31use crate::retention::Retention;
32use crate::transformation::Transformation;
33
34/// What the access did to the data.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36#[non_exhaustive]
37pub enum Action {
38    /// A query over the data, an ad hoc or a stored one.
39    Query,
40    /// A read of one resource.
41    Read,
42    /// The creation of a resource.
43    Create,
44    /// The update of a resource.
45    Update,
46    /// The deletion of a resource.
47    Delete,
48}
49
50/// How the access ended.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52#[non_exhaustive]
53pub enum Outcome {
54    /// The data were delivered or written as asked.
55    Success,
56    /// The access was answered with a failure, or with less than asked.
57    MinorFailure,
58    /// The access failed on the side that records it.
59    SeriousFailure,
60    /// The access failed and left the recording system unavailable.
61    MajorFailure,
62}
63
64/// One purpose of use the accessor declared: a code and the system that
65/// defines it.
66#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
67pub struct Purpose {
68    /// The code system, when one is named.
69    pub system: Option<String>,
70    /// The code.
71    pub code: String,
72}
73
74/// Who acts behind the token an access was authenticated by.
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76#[non_exhaustive]
77pub enum Acting {
78    /// The natural person the token's `sub` names.
79    Person,
80    /// A client application, which the recording system admits to personal
81    /// data only for the professional the token names.
82    Client,
83}
84
85impl Acting {
86    /// The code a record writes the acting mode as: `person` or `client`.
87    #[must_use]
88    pub const fn code(self) -> &'static str {
89        match self {
90            Self::Person => "person",
91            Self::Client => "client",
92        }
93    }
94}
95
96/// An assurance level of an electronic identification means, as Regulation
97/// (EU) No 910/2014 Art 8(2) names them, lowest first.
98#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
99pub enum AssuranceLevel {
100    /// Level low, Art 8(2)(a).
101    Low,
102    /// Level substantial, Art 8(2)(b).
103    Substantial,
104    /// Level high, Art 8(2)(c).
105    High,
106}
107
108impl AssuranceLevel {
109    /// The code a record writes the level as: `low`, `substantial` or
110    /// `high`.
111    #[must_use]
112    pub const fn code(self) -> &'static str {
113        match self {
114            Self::Low => "low",
115            Self::Substantial => "substantial",
116            Self::High => "high",
117        }
118    }
119}
120
121/// The professional's identification, as the token the access was
122/// authenticated by states it.
123///
124/// `Debug` shows which members are present and none of their values.
125#[derive(Clone, Default, PartialEq, Eq)]
126pub struct Professional {
127    /// The professional's name.
128    pub name: Option<String>,
129    /// The identifier their national authority issued them as a health
130    /// professional.
131    pub identifier: Option<String>,
132}
133
134impl fmt::Debug for Professional {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        f.debug_struct("Professional")
137            .field("name", &self.name.is_some())
138            .field("identifier", &self.identifier.is_some())
139            .finish()
140    }
141}
142
143/// A coded value, such as a professional role: a code and the system that
144/// defines it.
145#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
146pub struct Coded {
147    /// The code system, when one is named.
148    pub system: Option<String>,
149    /// The code.
150    pub code: String,
151}
152
153/// What a national contact point for digital health relays with a request.
154///
155/// The health professional and the healthcare provider of another Member
156/// State, as the contact point asserts them (Implementing Regulation (EU)
157/// 2026/2099 Art 7 and its Annex Tables 1 and 2). The recording system verified the contact point, never the professional:
158/// Art 6(1) and (2) give their identification and authentication to the
159/// entity the Member State of the professional lists. `Debug` shows the
160/// country and none of the other values.
161#[derive(Clone, PartialEq, Eq)]
162pub struct Relayed {
163    /// The contact point that asserted them, by the issuer of its token.
164    pub contact_point: String,
165    /// `country_code`: the ISO 3166-1 alpha-2 code of the Member State that
166    /// issued the professional's identification data.
167    pub country_code: String,
168    /// The health professional (Annex Table 1).
169    pub professional: RelayedProfessional,
170    /// The healthcare provider (Annex Table 2).
171    pub provider: RelayedProvider,
172}
173
174impl fmt::Debug for Relayed {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        f.debug_struct("Relayed")
177            .field("country_code", &self.country_code)
178            .finish_non_exhaustive()
179    }
180}
181
182/// The health professional a contact point relays (2026/2099 Annex
183/// Table 1).
184#[derive(Clone, PartialEq, Eq)]
185pub struct RelayedProfessional {
186    /// `family_name`.
187    pub family_name: String,
188    /// `given_name`.
189    pub given_name: String,
190    /// `hp_identifier`, unique among the identifiers the Member State where
191    /// the professional is registered issues.
192    pub identifier: String,
193    /// `issuing_authority_name`: the agency that issued `hp_identifier`.
194    pub issuing_authority: String,
195    /// `hp_professional_role`, as the codes the contact point states.
196    pub roles: Vec<Coded>,
197}
198
199impl fmt::Debug for RelayedProfessional {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        f.debug_struct("RelayedProfessional")
202            .field("roles", &self.roles.len())
203            .finish_non_exhaustive()
204    }
205}
206
207/// The healthcare provider a contact point relays (2026/2099 Annex
208/// Table 2), where the professional provides the treatment.
209#[derive(Clone, PartialEq, Eq)]
210pub struct RelayedProvider {
211    /// `healthcare_provider_identifier`, unique in the Member State that
212    /// issued it.
213    pub identifier: String,
214    /// `issuing_authority_name`: the agency that issued the identifier.
215    pub issuing_authority: String,
216    /// `healthcare_provider_name`.
217    pub name: String,
218    /// `healthcare_provider_address`: the official registered full address.
219    pub address: String,
220}
221
222impl fmt::Debug for RelayedProvider {
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224        f.debug_struct("RelayedProvider").finish_non_exhaustive()
225    }
226}
227
228/// Who accessed the data (Annex II 3.2(a), (b)).
229///
230/// The person, as the token the access was authenticated by names them, or
231/// as a local account names them when no token did, the client they used,
232/// and the provider they act for.
233///
234/// Each value is the authentication's, as the recording system verified it:
235/// the record infers none, so the issuer, the client, the acting mode and the
236/// assurance level are absent unless the authentication established them.
237#[derive(Clone, PartialEq, Eq)]
238pub struct Accessor {
239    /// The issuer that vouched for the person (`iss`), when a token named
240    /// them; `None` for a user authenticated otherwise, such as by HTTP
241    /// Basic.
242    pub issuer: Option<String>,
243    /// The person (`sub`, or the local account).
244    pub subject: String,
245    /// The client application they used (`client_id`), when a token named
246    /// one.
247    pub client_id: Option<String>,
248    /// The audience the token named the recording system by.
249    pub audience: Option<String>,
250    /// The healthcare provider or other organisation the person acts for.
251    pub provider: Option<String>,
252    /// Who acts behind the token: the person, or a client application, when
253    /// the recording system established it.
254    pub acting: Option<Acting>,
255    /// The roles or authorities the person acted under, such as the security
256    /// roles of the recording system, which the record writes at
257    /// `agent[user].role` (BALP 1.1.4 ยง3:5.7.5.4, the IUA `subject_role`).
258    pub roles: Vec<Coded>,
259    /// The identity of the access token (`jti`), when it names one.
260    pub token_id: Option<String>,
261    /// The assurance level the authentication reached, when the recording
262    /// system established one from the token.
263    pub assurance: Option<AssuranceLevel>,
264    /// The professional the token names (3.2(b)), who acts in person or
265    /// whom a client acts for.
266    pub professional: Professional,
267    /// Another identification of the person the token states beside its
268    /// `sub`, such as a professional number a claim of the deployment's
269    /// naming carries.
270    pub alt_id: Option<String>,
271    /// Every purpose of use the token declares.
272    pub purposes: Vec<Purpose>,
273    /// The identifier of every policy or plan the access was authorised
274    /// under beside the token, such as the data permit of a health data
275    /// access body (Art 68) a secondary-use caller declares it acts under,
276    /// in order. The BALP record writes each at `agent[user].policy` (FHIR R4
277    /// `AuditEvent.agent.policy`).
278    pub policies: Vec<String>,
279    /// The professional and the provider of another Member State a national
280    /// contact point relays, marked as asserted by it, when the client is
281    /// one (Implementing Regulation (EU) 2026/2099 Art 7).
282    pub relayed: Option<Relayed>,
283}
284
285impl fmt::Debug for Accessor {
286    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287        f.debug_struct("Accessor")
288            .field("provider", &self.provider.is_some())
289            .field("acting", &self.acting)
290            .field("roles", &self.roles)
291            .field("token_id", &self.token_id.is_some())
292            .field("assurance", &self.assurance)
293            .field("professional", &self.professional)
294            .field("alt_id", &self.alt_id.is_some())
295            .field("purposes", &self.purposes)
296            .field("policies", &self.policies.len())
297            .field("relayed", &self.relayed)
298            .finish_non_exhaustive()
299    }
300}
301
302/// The patient an access concerned, by the identifier the request named and
303/// the namespace that issued it; `Debug` shows the namespace alone.
304#[derive(Debug, Clone)]
305pub struct PatientIdentifier {
306    /// The issuing namespace, when one is known: a URI, which the record
307    /// writes as the identifier's system, or another name, such as an openEHR
308    /// `PARTY_REF.namespace`, which it writes beside the identifier.
309    pub namespace: Option<String>,
310    /// The identifier.
311    pub value: SecretString,
312}
313
314/// Whether the patient behind an `ehr_id` is named, so a search by the
315/// patient's identifier finds the access (Art 9(1)).
316///
317/// `Debug` shows the outcome and how many identifiers were found, never one.
318#[derive(Clone)]
319#[non_exhaustive]
320pub enum PatientLookup {
321    /// The request named the patient, so the record names them already.
322    RequestNamed,
323    /// The identity service holds these identifiers for the patient.
324    Found(Vec<PatientIdentifier>),
325    /// The identity service holds no identifier for the patient in a
326    /// namespace asked.
327    NotFound,
328    /// The identity service could not be asked, or did not answer.
329    Unavailable,
330    /// The recording system names no namespace to ask in.
331    NotConfigured,
332    /// The recording system has no identity service that names a patient
333    /// by an `ehr_id`.
334    Unsupported,
335}
336
337impl PatientLookup {
338    /// The outcome as a record writes it: `request-named`, `found`,
339    /// `not-found`, `unavailable`, `not-configured` or `unsupported`.
340    #[must_use]
341    pub const fn code(&self) -> &'static str {
342        match self {
343            Self::RequestNamed => "request-named",
344            Self::Found(_) => "found",
345            Self::NotFound => "not-found",
346            Self::Unavailable => "unavailable",
347            Self::NotConfigured => "not-configured",
348            Self::Unsupported => "unsupported",
349        }
350    }
351}
352
353impl fmt::Debug for PatientLookup {
354    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355        match self {
356            Self::Found(found) => write!(f, "Found({})", found.len()),
357            other => f.write_str(other.code()),
358        }
359    }
360}
361
362/// One `ehr_id` an access reached, at the endpoint that holds it, with
363/// whether the patient it belongs to is named.
364#[derive(Clone)]
365pub struct EhrAt {
366    /// The endpoint.
367    pub endpoint: String,
368    /// The `ehr_id` at that endpoint.
369    pub ehr_id: String,
370    /// Whether the patient behind the `ehr_id` is named.
371    pub patient: PatientLookup,
372}
373
374impl fmt::Debug for EhrAt {
375    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376        f.debug_struct("EhrAt")
377            .field("endpoint", &self.endpoint)
378            .field("patient", &self.patient)
379            .finish_non_exhaustive()
380    }
381}
382
383/// Whose data an access concerned: the patient the request named, each
384/// `ehr_id` the access reached, and, for an access over many patients' data
385/// that names none of them, the scope it read.
386#[derive(Clone, Default)]
387pub struct DataSubject {
388    /// The patient, when the request named one.
389    pub patient: Option<PatientIdentifier>,
390    /// The `ehr_id`s the access reached.
391    pub ehrs: Vec<EhrAt>,
392    /// The scope of an access over many patients' data whose record names
393    /// none of them: a population access.
394    pub population: Option<Population>,
395}
396
397impl fmt::Debug for DataSubject {
398    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399        f.debug_struct("DataSubject")
400            .field("patient", &self.patient.is_some())
401            .field("ehrs", &self.ehrs.len())
402            .field("population", &self.population)
403            .finish()
404    }
405}
406
407/// Why the record of an access over many patients' data names none of the
408/// `ehr_id`s whose records it read.
409#[derive(Debug, Clone, Copy, PartialEq, Eq)]
410#[non_exhaustive]
411pub enum PopulationReason {
412    /// The query projects no `ehr_id`, so no delivered row says whose
413    /// record it came from.
414    NotProjected,
415    /// The delivered rows name more `ehr_id`s than a record names.
416    OverLimit,
417    /// No row was delivered, so no row names an `ehr_id`.
418    NotDelivered,
419    /// A delivered `ehr_id` was in no origin's own answer, so the record
420    /// cannot say which origin holds it.
421    Unattributed,
422}
423
424impl PopulationReason {
425    /// The reason as a record writes it: `not-projected`, `over-limit`,
426    /// `not-delivered` or `unattributed`.
427    #[must_use]
428    pub const fn code(self) -> &'static str {
429        match self {
430            Self::NotProjected => "not-projected",
431            Self::OverLimit => "over-limit",
432            Self::NotDelivered => "not-delivered",
433            Self::Unattributed => "unattributed",
434        }
435    }
436}
437
438/// The scope of a population access: what the access read, when the record
439/// names none of the persons whose records it read.
440///
441/// The origins the access asked are the record's origins. A person's
442/// search finds the record through an origin that holds their record, and
443/// it says that a query over that origin ran, never that it read their
444/// record. `Debug` shows the reason and the counts, never an id.
445#[derive(Clone, PartialEq, Eq)]
446pub struct Population {
447    /// Why the record names no `ehr_id`.
448    pub reason: PopulationReason,
449    /// How many `ehr_id`s the delivered rows named, when they were read.
450    pub ehrs: Option<usize>,
451    /// The most `ehr_id`s a record names.
452    pub limit: usize,
453    /// The template ids the query constrains its data to.
454    pub templates: BTreeSet<String>,
455    /// The archetype ids the query constrains its data to.
456    pub archetypes: BTreeSet<String>,
457}
458
459impl fmt::Debug for Population {
460    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461        f.debug_struct("Population")
462            .field("reason", &self.reason)
463            .field("ehrs", &self.ehrs)
464            .field("limit", &self.limit)
465            .field("templates", &self.templates.len())
466            .field("archetypes", &self.archetypes.len())
467            .finish()
468    }
469}
470
471/// One origin of the data (Annex II 3.2(e)).
472///
473/// An endpoint the access asked, with how it answered, or the system stored
474/// data came from, such as the originating system an openEHR `FEEDER_AUDIT`
475/// names, or both.
476#[derive(Clone, PartialEq, Eq)]
477pub struct Origin {
478    /// The endpoint the access asked, as the registry names it, when it
479    /// asked one.
480    pub endpoint: Option<String>,
481    /// The node behind it.
482    pub node: Option<String>,
483    /// The openEHR `system_id` of the node, or of the system stored data
484    /// came from.
485    pub system_id: Option<String>,
486    /// How the endpoint answered, such as `active` or `node-error`, when an
487    /// endpoint was asked.
488    pub status: Option<String>,
489    /// The rows or objects it contributed, when counted.
490    pub rows: Option<usize>,
491    /// The version a write left at the endpoint, as its answer named it, or
492    /// `None` for an access that wrote nothing there.
493    pub version: Option<WrittenVersion>,
494    /// The `ehr_id` an EHR create left at the endpoint, as its answer named
495    /// it, or `None` for an access that created no EHR there.
496    pub created_ehr: Option<CreatedEhr>,
497    /// The categories of what it contributed, when told apart from the
498    /// other origins'.
499    pub categories: Option<Classification>,
500}
501
502impl Origin {
503    /// What the origin is keyed by for retention and labels: its endpoint,
504    /// or its `system_id` when it names no endpoint.
505    #[must_use]
506    pub fn key(&self) -> Option<&str> {
507        self.endpoint.as_deref().or(self.system_id.as_deref())
508    }
509}
510
511impl fmt::Debug for Origin {
512    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513        f.debug_struct("Origin")
514            .field("endpoint", &self.endpoint)
515            .field("node", &self.node)
516            .field("status", &self.status)
517            .field("rows", &self.rows)
518            .field("version", &self.version)
519            .field("created_ehr", &self.created_ehr)
520            .field("categories", &self.categories)
521            .finish_non_exhaustive()
522    }
523}
524
525/// The version a write left at its origin, which is the data a write
526/// accessed (Annex II 3.2), as the origin's answer named it.
527///
528/// The recording system holds the version id to the `OBJECT_VERSION_ID`
529/// form before it is recorded, so a value the answer carried in its place,
530/// whatever it holds, never reaches the record.
531#[derive(Clone, PartialEq, Eq)]
532#[non_exhaustive]
533pub enum WrittenVersion {
534    /// The answer named this `OBJECT_VERSION_ID`.
535    Named(String),
536    /// The answer named no version, so the record cannot tell which one the
537    /// write left.
538    Unnamed,
539    /// The answer named a version that is no `OBJECT_VERSION_ID`, so the
540    /// record cannot tell which one the write left.
541    Malformed,
542}
543
544impl WrittenVersion {
545    /// The version id, when the answer named one.
546    #[must_use]
547    pub fn named(&self) -> Option<&str> {
548        match self {
549            Self::Named(version) => Some(version),
550            Self::Unnamed | Self::Malformed => None,
551        }
552    }
553
554    /// Why the record cannot tell the version, as a record writes it:
555    /// `unnamed` or `malformed`, or `None` when the answer named it.
556    #[must_use]
557    pub fn unread(&self) -> Option<&'static str> {
558        match self {
559            Self::Named(_) => None,
560            Self::Unnamed => Some("unnamed"),
561            Self::Malformed => Some("malformed"),
562        }
563    }
564}
565
566impl fmt::Debug for WrittenVersion {
567    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
568        match self {
569            Self::Named(_) => f.write_str("Named(..)"),
570            Self::Unnamed => f.write_str("Unnamed"),
571            Self::Malformed => f.write_str("Malformed"),
572        }
573    }
574}
575
576/// The `ehr_id` of the EHR a create left at its origin, as the origin's
577/// answer named it.
578///
579/// The recording system holds the `ehr_id` to the `HIER_OBJECT_ID` form
580/// before it is recorded, so a value the answer carried in its place,
581/// whatever it holds, never reaches the record.
582#[derive(Clone, PartialEq, Eq)]
583#[non_exhaustive]
584pub enum CreatedEhr {
585    /// The answer named this `ehr_id`.
586    Named(String),
587    /// The answer named no `ehr_id`, so the record cannot tell which EHR the
588    /// create left.
589    Unnamed,
590    /// The answer named an `ehr_id` that is no `HIER_OBJECT_ID`, or two
591    /// that differ, so the record cannot tell which EHR the create left.
592    Malformed,
593}
594
595impl CreatedEhr {
596    /// The `ehr_id`, when the answer named one.
597    #[must_use]
598    pub fn named(&self) -> Option<&str> {
599        match self {
600            Self::Named(ehr_id) => Some(ehr_id),
601            Self::Unnamed | Self::Malformed => None,
602        }
603    }
604
605    /// Why the record cannot tell the `ehr_id`, as a record writes it:
606    /// `unnamed` or `malformed`, or `None` when the answer named it.
607    #[must_use]
608    pub fn unread(&self) -> Option<&'static str> {
609        match self {
610            Self::Named(_) => None,
611            Self::Unnamed => Some("unnamed"),
612            Self::Malformed => Some("malformed"),
613        }
614    }
615}
616
617impl fmt::Debug for CreatedEhr {
618    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
619        match self {
620            Self::Named(_) => f.write_str("Named(..)"),
621            Self::Unnamed => f.write_str("Unnamed"),
622            Self::Malformed => f.write_str("Malformed"),
623        }
624    }
625}
626
627/// What the access asked for.
628#[derive(Clone)]
629pub struct Request {
630    /// The id the recording system knows the request by, which its own log
631    /// names too, when the request carried one.
632    pub id: Option<String>,
633    /// The operation, such as an ITS-REST `operationId`.
634    pub operation: String,
635    /// The code system the operation is a code of, such as the system of the
636    /// ITS-REST operation ids: when it is named, the record writes the
637    /// operation as a `subtype` in it.
638    pub operation_system: Option<String>,
639    /// The resource the request addressed, when it addressed one.
640    pub resource: Option<String>,
641    /// The query text, for a query.
642    pub query: Option<SecretString>,
643    /// The name of the stored query, for a stored-query execution.
644    pub stored_query: Option<String>,
645    /// The address the request came from, when known.
646    pub client_address: Option<IpAddr>,
647    /// The identifier the client correlates the request by in its own
648    /// log, such as a national contact point's exchange log, when it sent
649    /// one.
650    pub correlation: Option<String>,
651}
652
653impl fmt::Debug for Request {
654    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655        f.debug_struct("Request")
656            .field("id", &self.id)
657            .field("operation", &self.operation)
658            .field("query", &self.query.is_some())
659            .field("correlation", &self.correlation.is_some())
660            .finish_non_exhaustive()
661    }
662}
663
664/// One access event (Annex II 3.2).
665#[derive(Clone)]
666pub struct AccessRecord {
667    /// What the access did.
668    pub action: Action,
669    /// When it ended (3.2(d)).
670    pub recorded: Timestamp,
671    /// How it ended.
672    pub outcome: Outcome,
673    /// Free text on how it ended, when the recording system gives one.
674    pub outcome_description: Option<String>,
675    /// Who accessed the data (3.2(a), (b)), with their purposes of use.
676    pub accessor: Accessor,
677    /// Whose data.
678    pub subject: DataSubject,
679    /// The categories of the data accessed (3.2(c)).
680    pub categories: Classification,
681    /// The rows or objects delivered to the accessor, when counted.
682    pub delivered: Option<usize>,
683    /// The origins of the data (3.2(e)), in the order the access asked them.
684    pub origins: Vec<Origin>,
685    /// How many distinct origins the access reached, when `origins` names
686    /// fewer, such as when the recording system bounds one record's origins.
687    pub origin_count: Option<usize>,
688    /// What the access asked for.
689    pub request: Request,
690    /// How long the record is kept (Art 9(2), Annex II 3.4).
691    pub retention: Retention,
692    /// The security labels the record carries for its categories and its
693    /// origins, so the store that holds it can limit who reads it (Annex II
694    /// 3.4).
695    pub labels: Labels,
696    /// The mark of an access the accessor declared, through its purpose of
697    /// use, as made to protect the vital interests of the data subject (Art
698    /// 11(5)), when it did.
699    pub emergency: Option<Emergency>,
700    /// Who inserted the information the access wrote under the data
701    /// subject's own grant, which the written data marks as inserted by
702    /// them (Art 5), when it did.
703    pub inserted: Option<Insertion>,
704    /// Each semantic transformation the access applied between openEHR and
705    /// the exchange format (eHealth Network General Guidelines Release 3.1
706    /// Art 6(3)), in the order it applied them.
707    pub transformations: Vec<Transformation>,
708}
709
710/// Who inserted the information a write carried under the data subject's
711/// own grant, as the written data marks it (Art 5).
712#[derive(Debug, Clone, Copy, PartialEq, Eq)]
713#[non_exhaustive]
714pub enum Insertion {
715    /// The data subject, in person.
716    Subject,
717    /// A natural person who acts for the data subject, a proxy or a legal
718    /// representative (Art 4(2)), whom the written data names.
719    Representative,
720}
721
722impl fmt::Debug for AccessRecord {
723    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
724        f.debug_struct("AccessRecord")
725            .field("action", &self.action)
726            .field("recorded", &self.recorded)
727            .field("outcome", &self.outcome)
728            .field("outcome_description", &self.outcome_description.is_some())
729            .field("accessor", &self.accessor)
730            .field("subject", &self.subject)
731            .field("categories", &self.categories)
732            .field("delivered", &self.delivered)
733            .field("origins", &self.origins)
734            .field("origin_count", &self.origin_count)
735            .field("request", &self.request)
736            .field("retention", &self.retention)
737            .field("labels", &self.labels)
738            .field("emergency", &self.emergency)
739            .field("inserted", &self.inserted)
740            .field("transformations", &self.transformations)
741            .finish()
742    }
743}