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}