fhir-types 0.1.127

Generated per-version FHIR types and terminology operation contracts
Documentation
// @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.
// Change the emitter (tools/fhir-codegen) and regenerate.
// SPDX-FileCopyrightText: Cadasto B.V.
// SPDX-License-Identifier: Apache-2.0
//! The element table of a FHIR version: every emitted type's elements in
//! definition order.
//!
//! The table states, per element, the `ElementDefinition` path, cardinality,
//! type codes and content reference, plus the kind each representation reads
//! (<https://hl7.org/fhir/R4/elementdefinition.html>). It is the one element
//! model the crate carries: the XML codec walks it, and so does a consumer
//! driving a path expression over the JSON object model. Every version module
//! emits its own `SCHEMAS` static over these types.
//!
//! The table carries `Element` itself (<https://hl7.org/fhir/R4/element.html>)
//! although no emitted field targets it: the FHIR JSON representation writes a
//! primitive's `id` and `extension` in a sibling member named with a leading
//! underscore (<https://hl7.org/fhir/R4/json.html>), and that member's shape is
//! `Element`.
//!
//! Every type names its base type, and the table carries each abstract type a
//! base chain passes through (`Resource`, `DomainResource`, `BackboneElement`,
//! and from R5 `Base`, `DataType` and `BackboneType`), so
//! [`Schemas::base_of`] walks every type to the root of the hierarchy. A
//! primitive has no element entry: [`Schemas::primitives`] names each one
//! with the type its `baseDefinition` names (`code` specializes `string`),
//! which [`Schemas::primitive_base_of`] reads. A constraint profile such as
//! `SimpleQuantity` is no type of its own and is in neither list.

/// The scalar a primitive's `value` attribute carries in JSON.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueKind {
    /// A JSON boolean.
    Boolean,
    /// A JSON number without a fraction.
    Integer,
    /// A JSON number keeping its lexical form.
    Decimal,
    /// A JSON string (every other primitive, `integer64` included).
    Text,
}

/// How an element travels in XML.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
    /// A plain string carried as an attribute (`Element.id`, `Extension.url`).
    Attribute,
    /// A primitive element: the `value` attribute, an `id` attribute, and
    /// `extension` children.
    Primitive(ValueKind),
    /// A complex element of the named type.
    Complex(&'static str),
    /// A resource inside its wrapper element.
    Resource,
    /// The XHTML narrative, passed through as text.
    Xhtml,
    /// A choice element: the name is the stem, the suffix names the type.
    Choice(&'static [(&'static str, Kind)]),
}

/// One element of a type, in definition order.
///
/// The cardinality, the path, the type codes and their target profiles, the
/// content reference and the two flags are the `ElementDefinition` fields of
/// the definition this element comes from
/// (<https://hl7.org/fhir/R4/elementdefinition.html>).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FieldSchema {
    /// The element name (the JSON key; a choice's stem).
    pub name: &'static str,
    /// The element path, for example `Observation.value[x]`.
    pub path: &'static str,
    /// How it travels.
    pub kind: Kind,
    /// The minimum cardinality.
    pub min: u32,
    /// The maximum cardinality; `None` is `*`.
    pub max: Option<u32>,
    /// Whether it repeats.
    pub many: bool,
    /// The type codes the definition lists, in its order; a choice lists its
    /// alternatives and a content reference lists none.
    pub types: &'static [&'static str],
    /// The canonical URLs the definition's types list in `targetProfile`, in
    /// its order: the resources a `Reference` admits and the definitions a
    /// `canonical` admits
    /// (<https://hl7.org/fhir/R4/elementdefinition-definitions.html#ElementDefinition.type.targetProfile>).
    pub target_profiles: &'static [&'static str],
    /// The element path a `contentReference` names, without the leading `#`.
    pub content_reference: Option<&'static str>,
    /// Whether the element is part of the summary view, the `isSummary` flag a
    /// `_summary=true` answer keeps (<https://hl7.org/fhir/R4/search.html#summary>).
    pub is_summary: bool,
    /// Whether the element modifies the meaning of its parent, the `isModifier`
    /// flag (<https://hl7.org/fhir/R4/conformance-rules.html#isModifier>).
    pub is_modifier: bool,
}

/// One alternative of a choice element: the suffix its JSON member carries,
/// the type code the definition lists for it, and how it travels.
///
/// The JSON member of the alternative is the choice's stem followed by the
/// suffix, the type code with its first letter in upper case
/// (`valueQuantity`, <https://hl7.org/fhir/R4/json.html#2.6.2>).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ChoiceForm {
    /// The suffix of the JSON member name, for example `Quantity`.
    pub suffix: &'static str,
    /// The type code the definition lists, for example `dateTime`.
    pub code: &'static str,
    /// How the alternative travels.
    pub kind: Kind,
}

/// A JSON member name resolved against the elements of a type.
///
/// The JSON representation names an element by its name, a choice element by
/// its stem and the suffix of one alternative, and the `id` and `extension` of
/// a primitive element by its name behind a leading underscore
/// (<https://hl7.org/fhir/R4/json.html#2.6.2>).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Member {
    /// The element the member belongs to.
    pub field: &'static FieldSchema,
    /// How the member's value travels: the chosen alternative's kind for a
    /// choice form, the element's own kind otherwise.
    pub kind: Kind,
    /// The type code of the chosen alternative, for a choice form.
    pub choice: Option<&'static str>,
    /// Whether the member is the `_` sibling carrying a primitive's `id` and
    /// `extension`.
    pub sibling: bool,
}

impl FieldSchema {
    /// The alternatives of a choice element, in definition order; none for an
    /// element of a single type.
    pub fn choices(&self) -> impl Iterator<Item = ChoiceForm> + '_ {
        let variants: &'static [(&'static str, Kind)] = match self.kind {
            Kind::Choice(variants) => variants,
            _ => &[],
        };
        variants.iter().filter_map(|(suffix, kind)| {
            self.choice_type(suffix).map(|code| ChoiceForm {
                suffix,
                code,
                kind: *kind,
            })
        })
    }

    /// The type code of the choice alternative whose JSON suffix is `suffix`,
    /// `None` when the element lists no such type.
    #[must_use]
    pub fn choice_type(&self, suffix: &str) -> Option<&'static str> {
        self.types
            .iter()
            .copied()
            .find(|code| is_choice_suffix(code, suffix))
    }
}

/// Whether `suffix` is `code` with its first letter in upper case, the suffix
/// a choice form carries (<https://hl7.org/fhir/R4/json.html#2.6.2>).
fn is_choice_suffix(code: &str, suffix: &str) -> bool {
    let mut code_chars = code.chars();
    let mut suffix_chars = suffix.chars();
    match (code_chars.next(), suffix_chars.next()) {
        (Some(first), Some(upper)) => {
            first.to_ascii_uppercase() == upper && code_chars.as_str() == suffix_chars.as_str()
        }
        _ => false,
    }
}

/// One type's elements.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TypeSchema {
    /// The type name (a resource's element name).
    pub name: &'static str,
    /// The path the type is defined at: its own name for a resource or a
    /// datatype, the element path for a backbone element.
    pub path: &'static str,
    /// The elements, in definition order.
    pub fields: &'static [FieldSchema],
    /// The name of the type this one specializes, its
    /// `StructureDefinition.baseDefinition`
    /// (<https://hl7.org/fhir/R4/structuredefinition-definitions.html#StructureDefinition.baseDefinition>);
    /// for a backbone element, the type its element names, `BackboneElement`
    /// or `Element`. `None` at the root of the hierarchy.
    pub base: Option<&'static str>,
}

/// One primitive type and the type it specializes.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PrimitiveSchema {
    /// The type code (`code`, `positiveInt`).
    pub name: &'static str,
    /// The type its `StructureDefinition.baseDefinition` names
    /// (<https://hl7.org/fhir/R4/structuredefinition-definitions.html#StructureDefinition.baseDefinition>):
    /// another primitive (`code` specializes `string`), or the type at the
    /// top of a primitive's chain: `Element` until R4B, which the table
    /// carries, and from R5 the abstract `PrimitiveType`, which it does not.
    /// `None` when the definition names none.
    pub base: Option<&'static str>,
}

impl TypeSchema {
    /// The element a JSON member of this type's object names, with the
    /// alternative a choice form selects.
    ///
    /// `key` is an element name, a choice element's stem followed by the
    /// suffix of one alternative, or either of those behind a leading
    /// underscore when the element or alternative is a primitive
    /// (<https://hl7.org/fhir/R4/json.html#2.6.2>). A choice element's bare
    /// stem names no member, and neither does a key the type does not define.
    #[must_use]
    pub fn member(&self, key: &str) -> Option<Member> {
        let (name, sibling) = match key.strip_prefix('_') {
            Some(name) => (name, true),
            None => (key, false),
        };
        let member = self.fields.iter().find_map(|field| match field.kind {
            Kind::Choice(_) => {
                let suffix = name.strip_prefix(field.name)?;
                field
                    .choices()
                    .find(|form| form.suffix == suffix)
                    .map(|form| Member {
                        field,
                        kind: form.kind,
                        choice: Some(form.code),
                        sibling,
                    })
            }
            kind => (field.name == name).then_some(Member {
                field,
                kind,
                choice: None,
                sibling,
            }),
        })?;
        (!sibling || matches!(member.kind, Kind::Primitive(_))).then_some(member)
    }
}

/// A version's schema: every type, sorted by name, the resource names and
/// the primitive types.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Schemas {
    /// The types, sorted by name.
    pub types: &'static [TypeSchema],
    /// The resource type names, sorted.
    pub resources: &'static [&'static str],
    /// The primitive types, sorted by name.
    pub primitives: &'static [PrimitiveSchema],
}

impl Schemas {
    /// The schema of the type named `name`.
    #[must_use]
    pub fn type_named(&self, name: &str) -> Option<&'static TypeSchema> {
        self.types
            .binary_search_by(|t| t.name.cmp(name))
            .ok()
            .and_then(|i| self.types.get(i))
    }

    /// The name of the type `name` specializes, `None` for a type at the root
    /// of the hierarchy and for a name the table does not carry.
    #[must_use]
    pub fn base_of(&self, name: &str) -> Option<&'static str> {
        self.type_named(name).and_then(|schema| schema.base)
    }

    /// The type the primitive `name` specializes, `None` for a code that names
    /// no primitive and for a primitive whose definition names no base.
    #[must_use]
    pub fn primitive_base_of(&self, name: &str) -> Option<&'static str> {
        self.primitives
            .binary_search_by(|p| p.name.cmp(name))
            .ok()
            .and_then(|i| self.primitives.get(i))
            .and_then(|primitive| primitive.base)
    }

    /// Whether the type `name` is `ancestor` or specializes it, directly or
    /// through its base chain: `Age` specializes `Quantity`, and `Patient`
    /// specializes `DomainResource` and `Resource`
    /// (<https://hl7.org/fhir/R5/types.html>).
    #[must_use]
    pub fn specializes(&self, name: &str, ancestor: &str) -> bool {
        if name == ancestor {
            return true;
        }
        let mut current = self.base_of(name);
        for _ in 0..self.types.len() {
            match current {
                Some(base) if base == ancestor => return true,
                Some(base) => current = self.base_of(base),
                None => return false,
            }
        }
        false
    }

    /// The type and element a dotted element path names.
    ///
    /// The walk starts at the resource or datatype the first segment names and
    /// follows each further segment through the complex and backbone types the
    /// elements hold, including the hop a `contentReference` makes
    /// (<https://hl7.org/fhir/R4/elementdefinition.html>). A choice element
    /// answers to its base path (`Observation.value[x]`) and to every expanded
    /// form the JSON representation gives it (`Observation.valueQuantity`,
    /// <https://hl7.org/fhir/R4/json.html>). A path that continues through a
    /// primitive, through a resource-typed element, or through the base form of
    /// a choice has no element here, and neither has a path the schema does not
    /// know.
    #[must_use]
    pub fn element(&self, path: &str) -> Option<(&'static TypeSchema, &'static FieldSchema)> {
        let (schema, field, _) = self.resolve(path)?;
        Some((schema, field))
    }

    /// The type an element path resolves to.
    ///
    /// A bare type name resolves to that type; an element path resolves to the
    /// complex or backbone type the element holds. A primitive element has no
    /// type here, because the schema carries only complex types.
    #[must_use]
    pub fn type_of(&self, path: &str) -> Option<&'static TypeSchema> {
        let Some((_, _, kind)) = self.resolve(path) else {
            return self.type_named(path).filter(|schema| schema.path == path);
        };
        match kind {
            Kind::Complex(name) => self.type_named(name),
            _ => None,
        }
    }

    /// The type, element, and effective kind a dotted element path names.
    ///
    /// The effective kind is the chosen variant's kind when the path names an
    /// expanded choice form, and the element's own kind otherwise.
    fn resolve(&self, path: &str) -> Option<(&'static TypeSchema, &'static FieldSchema, Kind)> {
        let (root, rest) = path.split_once('.')?;
        let mut schema = self.type_named(root).filter(|found| found.path == root)?;
        let mut segments = rest.split('.').peekable();
        while let Some(segment) = segments.next() {
            let (field, kind) = field_named(schema, segment)?;
            if segments.peek().is_none() {
                return Some((schema, field, kind));
            }
            let Kind::Complex(name) = kind else {
                return None;
            };
            schema = self.type_named(name)?;
        }
        None
    }

    /// Whether `name` is a resource type of this version.
    #[must_use]
    pub fn is_resource(&self, name: &str) -> bool {
        self.resources.binary_search(&name).is_ok()
    }
}

/// The element `segment` names in `schema`, with the kind that segment selects.
///
/// A segment is an element name, a choice element's base name with its `[x]`
/// suffix, or a choice element's name with the suffix of one alternative
/// (<https://hl7.org/fhir/R4/json.html>).
fn field_named(schema: &'static TypeSchema, segment: &str) -> Option<(&'static FieldSchema, Kind)> {
    let named = schema
        .fields
        .iter()
        .find(|field| field.name == segment || segment.strip_suffix("[x]") == Some(field.name));
    if let Some(field) = named {
        return Some((field, field.kind));
    }
    schema
        .member(segment)
        .filter(|member| !member.sibling)
        .map(|member| (member.field, member.kind))
}