fhir-types 0.1.106

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. DO NOT EDIT.
// Change the emitter (tools/fhir-codegen) and regenerate.
// SPDX-FileCopyrightText: Vernum Projecten 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`.

/// 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, 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 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 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],
}

/// A version's schema: every type, sorted by name, and the resource names.
#[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],
}

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 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.fields.iter().find_map(|field| {
        let Kind::Choice(variants) = field.kind else {
            return None;
        };
        let suffix = segment.strip_prefix(field.name)?;
        variants
            .iter()
            .find(|(candidate, _)| *candidate == suffix)
            .map(|(_, kind)| (field, *kind))
    })
}