Skip to main content

fhir_types/
schema.rs

1// @generated by fhir-codegen from hl7.fhir.r4.core 4.0.1, hl7.fhir.r4b.core 4.3.0, hl7.fhir.r5.core 5.0.0, hl7.fhir.r6.core 6.0.0-ballot5, hl7.fhir.r3.core 3.0.2. DO NOT EDIT.
2// Change the emitter (tools/fhir-codegen) and regenerate.
3// SPDX-FileCopyrightText: Cadasto B.V.
4// SPDX-License-Identifier: Apache-2.0
5//! The element table of a FHIR version: every emitted type's elements in
6//! definition order.
7//!
8//! The table states, per element, the `ElementDefinition` path, cardinality,
9//! type codes and content reference, plus the kind each representation reads
10//! (<https://hl7.org/fhir/R4/elementdefinition.html>). It is the one element
11//! model the crate carries: the XML codec walks it, and so does a consumer
12//! driving a path expression over the JSON object model. Every version module
13//! emits its own `SCHEMAS` static over these types.
14//!
15//! The table carries `Element` itself (<https://hl7.org/fhir/R4/element.html>)
16//! although no emitted field targets it: the FHIR JSON representation writes a
17//! primitive's `id` and `extension` in a sibling member named with a leading
18//! underscore (<https://hl7.org/fhir/R4/json.html>), and that member's shape is
19//! `Element`.
20//!
21//! Every type names its base type, and the table carries each abstract type a
22//! base chain passes through (`Resource`, `DomainResource`, `BackboneElement`,
23//! and from R5 `Base`, `DataType` and `BackboneType`), so
24//! [`Schemas::base_of`] walks every type to the root of the hierarchy. A
25//! primitive has no element entry: [`Schemas::primitives`] names each one
26//! with the type its `baseDefinition` names (`code` specializes `string`),
27//! which [`Schemas::primitive_base_of`] reads. A constraint profile such as
28//! `SimpleQuantity` is no type of its own and is in neither list.
29
30/// The scalar a primitive's `value` attribute carries in JSON.
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum ValueKind {
33    /// A JSON boolean.
34    Boolean,
35    /// A JSON number without a fraction.
36    Integer,
37    /// A JSON number keeping its lexical form.
38    Decimal,
39    /// A JSON string (every other primitive, `integer64` included).
40    Text,
41}
42
43/// How an element travels in XML.
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum Kind {
46    /// A plain string carried as an attribute (`Element.id`, `Extension.url`).
47    Attribute,
48    /// A primitive element: the `value` attribute, an `id` attribute, and
49    /// `extension` children.
50    Primitive(ValueKind),
51    /// A complex element of the named type.
52    Complex(&'static str),
53    /// A resource inside its wrapper element.
54    Resource,
55    /// The XHTML narrative, passed through as text.
56    Xhtml,
57    /// A choice element: the name is the stem, the suffix names the type.
58    Choice(&'static [(&'static str, Kind)]),
59}
60
61/// One element of a type, in definition order.
62///
63/// The cardinality, the path, the type codes and their target profiles, the
64/// content reference and the two flags are the `ElementDefinition` fields of
65/// the definition this element comes from
66/// (<https://hl7.org/fhir/R4/elementdefinition.html>).
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub struct FieldSchema {
69    /// The element name (the JSON key; a choice's stem).
70    pub name: &'static str,
71    /// The element path, for example `Observation.value[x]`.
72    pub path: &'static str,
73    /// How it travels.
74    pub kind: Kind,
75    /// The minimum cardinality.
76    pub min: u32,
77    /// The maximum cardinality; `None` is `*`.
78    pub max: Option<u32>,
79    /// Whether it repeats.
80    pub many: bool,
81    /// The type codes the definition lists, in its order; a choice lists its
82    /// alternatives and a content reference lists none.
83    pub types: &'static [&'static str],
84    /// The canonical URLs the definition's types list in `targetProfile`, in
85    /// its order: the resources a `Reference` admits and the definitions a
86    /// `canonical` admits
87    /// (<https://hl7.org/fhir/R4/elementdefinition-definitions.html#ElementDefinition.type.targetProfile>).
88    pub target_profiles: &'static [&'static str],
89    /// The element path a `contentReference` names, without the leading `#`.
90    pub content_reference: Option<&'static str>,
91    /// Whether the element is part of the summary view, the `isSummary` flag a
92    /// `_summary=true` answer keeps (<https://hl7.org/fhir/R4/search.html#summary>).
93    pub is_summary: bool,
94    /// Whether the element modifies the meaning of its parent, the `isModifier`
95    /// flag (<https://hl7.org/fhir/R4/conformance-rules.html#isModifier>).
96    pub is_modifier: bool,
97}
98
99/// One alternative of a choice element: the suffix its JSON member carries,
100/// the type code the definition lists for it, and how it travels.
101///
102/// The JSON member of the alternative is the choice's stem followed by the
103/// suffix, the type code with its first letter in upper case
104/// (`valueQuantity`, <https://hl7.org/fhir/R4/json.html#2.6.2>).
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub struct ChoiceForm {
107    /// The suffix of the JSON member name, for example `Quantity`.
108    pub suffix: &'static str,
109    /// The type code the definition lists, for example `dateTime`.
110    pub code: &'static str,
111    /// How the alternative travels.
112    pub kind: Kind,
113}
114
115/// A JSON member name resolved against the elements of a type.
116///
117/// The JSON representation names an element by its name, a choice element by
118/// its stem and the suffix of one alternative, and the `id` and `extension` of
119/// a primitive element by its name behind a leading underscore
120/// (<https://hl7.org/fhir/R4/json.html#2.6.2>).
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub struct Member {
123    /// The element the member belongs to.
124    pub field: &'static FieldSchema,
125    /// How the member's value travels: the chosen alternative's kind for a
126    /// choice form, the element's own kind otherwise.
127    pub kind: Kind,
128    /// The type code of the chosen alternative, for a choice form.
129    pub choice: Option<&'static str>,
130    /// Whether the member is the `_` sibling carrying a primitive's `id` and
131    /// `extension`.
132    pub sibling: bool,
133}
134
135impl FieldSchema {
136    /// The alternatives of a choice element, in definition order; none for an
137    /// element of a single type.
138    pub fn choices(&self) -> impl Iterator<Item = ChoiceForm> + '_ {
139        let variants: &'static [(&'static str, Kind)] = match self.kind {
140            Kind::Choice(variants) => variants,
141            _ => &[],
142        };
143        variants.iter().filter_map(|(suffix, kind)| {
144            self.choice_type(suffix).map(|code| ChoiceForm {
145                suffix,
146                code,
147                kind: *kind,
148            })
149        })
150    }
151
152    /// The type code of the choice alternative whose JSON suffix is `suffix`,
153    /// `None` when the element lists no such type.
154    #[must_use]
155    pub fn choice_type(&self, suffix: &str) -> Option<&'static str> {
156        self.types
157            .iter()
158            .copied()
159            .find(|code| is_choice_suffix(code, suffix))
160    }
161}
162
163/// Whether `suffix` is `code` with its first letter in upper case, the suffix
164/// a choice form carries (<https://hl7.org/fhir/R4/json.html#2.6.2>).
165fn is_choice_suffix(code: &str, suffix: &str) -> bool {
166    let mut code_chars = code.chars();
167    let mut suffix_chars = suffix.chars();
168    match (code_chars.next(), suffix_chars.next()) {
169        (Some(first), Some(upper)) => {
170            first.to_ascii_uppercase() == upper && code_chars.as_str() == suffix_chars.as_str()
171        }
172        _ => false,
173    }
174}
175
176/// One type's elements.
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
178pub struct TypeSchema {
179    /// The type name (a resource's element name).
180    pub name: &'static str,
181    /// The path the type is defined at: its own name for a resource or a
182    /// datatype, the element path for a backbone element.
183    pub path: &'static str,
184    /// The elements, in definition order.
185    pub fields: &'static [FieldSchema],
186    /// The name of the type this one specializes, its
187    /// `StructureDefinition.baseDefinition`
188    /// (<https://hl7.org/fhir/R4/structuredefinition-definitions.html#StructureDefinition.baseDefinition>);
189    /// for a backbone element, the type its element names, `BackboneElement`
190    /// or `Element`. `None` at the root of the hierarchy.
191    pub base: Option<&'static str>,
192}
193
194/// One primitive type and the type it specializes.
195#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196pub struct PrimitiveSchema {
197    /// The type code (`code`, `positiveInt`).
198    pub name: &'static str,
199    /// The type its `StructureDefinition.baseDefinition` names
200    /// (<https://hl7.org/fhir/R4/structuredefinition-definitions.html#StructureDefinition.baseDefinition>):
201    /// another primitive (`code` specializes `string`), or the type at the
202    /// top of a primitive's chain: `Element` until R4B, which the table
203    /// carries, and from R5 the abstract `PrimitiveType`, which it does not.
204    /// `None` when the definition names none.
205    pub base: Option<&'static str>,
206}
207
208impl TypeSchema {
209    /// The element a JSON member of this type's object names, with the
210    /// alternative a choice form selects.
211    ///
212    /// `key` is an element name, a choice element's stem followed by the
213    /// suffix of one alternative, or either of those behind a leading
214    /// underscore when the element or alternative is a primitive
215    /// (<https://hl7.org/fhir/R4/json.html#2.6.2>). A choice element's bare
216    /// stem names no member, and neither does a key the type does not define.
217    #[must_use]
218    pub fn member(&self, key: &str) -> Option<Member> {
219        let (name, sibling) = match key.strip_prefix('_') {
220            Some(name) => (name, true),
221            None => (key, false),
222        };
223        let member = self.fields.iter().find_map(|field| match field.kind {
224            Kind::Choice(_) => {
225                let suffix = name.strip_prefix(field.name)?;
226                field
227                    .choices()
228                    .find(|form| form.suffix == suffix)
229                    .map(|form| Member {
230                        field,
231                        kind: form.kind,
232                        choice: Some(form.code),
233                        sibling,
234                    })
235            }
236            kind => (field.name == name).then_some(Member {
237                field,
238                kind,
239                choice: None,
240                sibling,
241            }),
242        })?;
243        (!sibling || matches!(member.kind, Kind::Primitive(_))).then_some(member)
244    }
245}
246
247/// A version's schema: every type, sorted by name, the resource names and
248/// the primitive types.
249#[derive(Debug, Clone, Copy, PartialEq, Eq)]
250pub struct Schemas {
251    /// The types, sorted by name.
252    pub types: &'static [TypeSchema],
253    /// The resource type names, sorted.
254    pub resources: &'static [&'static str],
255    /// The primitive types, sorted by name.
256    pub primitives: &'static [PrimitiveSchema],
257}
258
259impl Schemas {
260    /// The schema of the type named `name`.
261    #[must_use]
262    pub fn type_named(&self, name: &str) -> Option<&'static TypeSchema> {
263        self.types
264            .binary_search_by(|t| t.name.cmp(name))
265            .ok()
266            .and_then(|i| self.types.get(i))
267    }
268
269    /// The name of the type `name` specializes, `None` for a type at the root
270    /// of the hierarchy and for a name the table does not carry.
271    #[must_use]
272    pub fn base_of(&self, name: &str) -> Option<&'static str> {
273        self.type_named(name).and_then(|schema| schema.base)
274    }
275
276    /// The type the primitive `name` specializes, `None` for a code that names
277    /// no primitive and for a primitive whose definition names no base.
278    #[must_use]
279    pub fn primitive_base_of(&self, name: &str) -> Option<&'static str> {
280        self.primitives
281            .binary_search_by(|p| p.name.cmp(name))
282            .ok()
283            .and_then(|i| self.primitives.get(i))
284            .and_then(|primitive| primitive.base)
285    }
286
287    /// Whether the type `name` is `ancestor` or specializes it, directly or
288    /// through its base chain: `Age` specializes `Quantity`, and `Patient`
289    /// specializes `DomainResource` and `Resource`
290    /// (<https://hl7.org/fhir/R5/types.html>).
291    #[must_use]
292    pub fn specializes(&self, name: &str, ancestor: &str) -> bool {
293        if name == ancestor {
294            return true;
295        }
296        let mut current = self.base_of(name);
297        for _ in 0..self.types.len() {
298            match current {
299                Some(base) if base == ancestor => return true,
300                Some(base) => current = self.base_of(base),
301                None => return false,
302            }
303        }
304        false
305    }
306
307    /// The type and element a dotted element path names.
308    ///
309    /// The walk starts at the resource or datatype the first segment names and
310    /// follows each further segment through the complex and backbone types the
311    /// elements hold, including the hop a `contentReference` makes
312    /// (<https://hl7.org/fhir/R4/elementdefinition.html>). A choice element
313    /// answers to its base path (`Observation.value[x]`) and to every expanded
314    /// form the JSON representation gives it (`Observation.valueQuantity`,
315    /// <https://hl7.org/fhir/R4/json.html>). A path that continues through a
316    /// primitive, through a resource-typed element, or through the base form of
317    /// a choice has no element here, and neither has a path the schema does not
318    /// know.
319    #[must_use]
320    pub fn element(&self, path: &str) -> Option<(&'static TypeSchema, &'static FieldSchema)> {
321        let (schema, field, _) = self.resolve(path)?;
322        Some((schema, field))
323    }
324
325    /// The type an element path resolves to.
326    ///
327    /// A bare type name resolves to that type; an element path resolves to the
328    /// complex or backbone type the element holds. A primitive element has no
329    /// type here, because the schema carries only complex types.
330    #[must_use]
331    pub fn type_of(&self, path: &str) -> Option<&'static TypeSchema> {
332        let Some((_, _, kind)) = self.resolve(path) else {
333            return self.type_named(path).filter(|schema| schema.path == path);
334        };
335        match kind {
336            Kind::Complex(name) => self.type_named(name),
337            _ => None,
338        }
339    }
340
341    /// The type, element, and effective kind a dotted element path names.
342    ///
343    /// The effective kind is the chosen variant's kind when the path names an
344    /// expanded choice form, and the element's own kind otherwise.
345    fn resolve(&self, path: &str) -> Option<(&'static TypeSchema, &'static FieldSchema, Kind)> {
346        let (root, rest) = path.split_once('.')?;
347        let mut schema = self.type_named(root).filter(|found| found.path == root)?;
348        let mut segments = rest.split('.').peekable();
349        while let Some(segment) = segments.next() {
350            let (field, kind) = field_named(schema, segment)?;
351            if segments.peek().is_none() {
352                return Some((schema, field, kind));
353            }
354            let Kind::Complex(name) = kind else {
355                return None;
356            };
357            schema = self.type_named(name)?;
358        }
359        None
360    }
361
362    /// Whether `name` is a resource type of this version.
363    #[must_use]
364    pub fn is_resource(&self, name: &str) -> bool {
365        self.resources.binary_search(&name).is_ok()
366    }
367}
368
369/// The element `segment` names in `schema`, with the kind that segment selects.
370///
371/// A segment is an element name, a choice element's base name with its `[x]`
372/// suffix, or a choice element's name with the suffix of one alternative
373/// (<https://hl7.org/fhir/R4/json.html>).
374fn field_named(schema: &'static TypeSchema, segment: &str) -> Option<(&'static FieldSchema, Kind)> {
375    let named = schema
376        .fields
377        .iter()
378        .find(|field| field.name == segment || segment.strip_suffix("[x]") == Some(field.name));
379    if let Some(field) = named {
380        return Some((field, field.kind));
381    }
382    schema
383        .member(segment)
384        .filter(|member| !member.sibling)
385        .map(|member| (member.field, member.kind))
386}