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