fhir-types 0.1.127

Generated per-version FHIR types and terminology operation contracts
Documentation
# fhir-types

Per-version FHIR types and terminology operation contracts.

Generated Rust types for the FHIR STU3 (3.0.2), R4 (4.0.1), R4B (4.3.0), R5
(5.0.0), and R6 (6.0.0-ballot5) terminology surface: `CodeSystem`, `ValueSet`,
`ConceptMap`, `Parameters`, `OperationOutcome`, `CapabilityStatement`,
`TerminologyCapabilities` (R4 and later; STU3 defines none), `Bundle`, the
datatypes and primitives they reach,
a JSON codec, and typed request and response contracts for `$lookup`,
`$validate-code`, `$expand`, `$subsumes`, and `$translate` per version, each
with a runtime descriptor of the declared parameter set. The `resources`
feature widens the same tree to every concrete resource each package defines,
with the complete closure of the datatypes those resources reference.

## Features

One tree holds every type the generator emits, and the features select inside
it. The default set is what a dependant gets with no feature list.

| Feature | What it enables | Default |
|---|---|---|
| `stu3` | the FHIR STU3 (3.0.2) module | yes |
| `r4` | the FHIR R4 (4.0.1) module | yes |
| `r4b` | the FHIR R4B (4.3.0) module | yes |
| `r5` | the FHIR R5 (5.0.0) module | yes |
| `r6` | the FHIR R6 (6.0.0-ballot5) module | yes |
| `terminology` | the terminology root set, the closure of the types it references, and the operation contracts | yes |
| `resources` | every concrete resource each package defines, with its complete closure; enables `terminology` | no |

A version module carries only the types the enabled root set selects, so
`default-features = false` with `r4` and `terminology` builds the R4
terminology surface alone. `Resource` holds one variant per resource the
enabled features select, and `UnknownResource` carries any other resource's
JSON body unchanged.

Every file is generated by [`fhir-codegen`](https://github.com/FerroHEALTH/FerroBRIDGE/tree/main/tools/fhir-codegen)
from the vendored HL7 packages and marked `// @generated`. To change the
output, change the generator and regenerate; the crate is never hand-edited.
The terminology ecosystem overlay (the HL7 terminology ecosystem IG requirements)
is applied at generation to R4, R4B and R5 and marked per parameter; the STU3
contracts carry exactly the parameters 3.0.2 declares.

## The search parameter registry

With `resources` on, each version module carries
`search_parameters::SEARCH_PARAMETERS`, over the types in the crate-level
`fhir_types::search` module: every `SearchParameter` the core package defines
(its examples left out) with its canonical, code, base types, type and
expression, and the standard parameter each resource type answers by code,
its own or one a base type defines.

```rust
use fhir_types::r4::search_parameters::SEARCH_PARAMETERS;

let code = SEARCH_PARAMETERS.of("Condition", "code").expect("a standard code");
assert_eq!(code.url, "http://hl7.org/fhir/SearchParameter/clinical-code");
assert_eq!(code.r#type.code(), "token");
let id = SEARCH_PARAMETERS.of("Condition", "_id").expect("defined on Resource");
assert_eq!(id.url, "http://hl7.org/fhir/SearchParameter/Resource-id");
```

## The element table

Each version module carries `schema::SCHEMAS`, the elements of every type it
emits in definition order, over the table types in the crate-level
`fhir_types::schema` module. Per element it holds the `ElementDefinition` path,
`min`, `max`, the type codes the definition lists, the `contentReference`
target, the `isSummary` and `isModifier` flags, and the kind the XML codec
reads. `Schemas::element` resolves a dotted
element path through complex types, backbone elements and content references,
and answers on a choice element's base path and on each expanded form the JSON
representation gives it; `Schemas::type_of` gives the type a path resolves to.

```rust
use fhir_types::r4::schema::SCHEMAS;

let (owner, element) = SCHEMAS
    .element("Parameters.parameter.valueQuantity")
    .expect("a defined element");
assert_eq!(owner.path, "Parameters.parameter");
assert_eq!(element.path, "Parameters.parameter.value[x]");
assert_eq!((element.min, element.max), (0, Some(1)));
assert_eq!(
    SCHEMAS
        .type_of("Parameters.parameter.valueQuantity")
        .map(|schema| schema.name),
    Some("Quantity"),
);
```

A type the enabled features leave out is absent from the table, so a path into
it resolves to `None`.

The table also carries `Element` (<https://hl7.org/fhir/R4/element.html>) with
its `id` and `extension`, although no emitted field targets it. FHIR JSON
writes a primitive's `id` and `extension` in a sibling member named with a
leading underscore (`_birthDate`, <https://hl7.org/fhir/R4/json.html>), and a
consumer walking a path resolves that member's contents against this entry. It
is a table-only entry: the crate emits no `Element` Rust type, because an
element whose type is `Element` becomes its own nested struct.

Every type names the type it specializes, and `Schemas::base_of` reads it.
A primitive has no element entry: `Schemas::primitives` lists each one with
the type its `StructureDefinition.baseDefinition` names, which
`Schemas::primitive_base_of` reads.

```rust
use fhir_types::r4::schema::SCHEMAS;

assert_eq!(SCHEMAS.primitive_base_of("code"), Some("string"));
assert_eq!(SCHEMAS.primitive_base_of("string"), Some("Element"));
```

## Primitive lexical forms

A primitive value the FHIR JSON representation carries as a string is held to
the lexical form of its type. The form is the `regex` extension the version's
own package puts on the primitive's `value` element, anchored to the whole
value and compiled once per primitive
(<https://hl7.org/fhir/R5/datatypes.html#primitive>). A value outside it is a
`DecodeError` carrying `DecodeErrorKind::BadValue` and the element path, so
`"date": "yesterday"` is refused at `CodeSystem.date` rather than stored and
read defensively later.

```rust
use fhir_types::codec::{DecodeErrorKind, Json, Path, expect_object};

let document = serde_json::from_str::<fhir_types::codec::Value>(
    r#"{"resourceType":"CodeSystem","status":"active","content":"complete","date":"yesterday"}"#,
)?;
let mut path = Path::root("CodeSystem");
let object = expect_object(&document, &path)?;
let error = fhir_types::r4::code_system::CodeSystem::from_json(object, &mut path)
    .expect_err("yesterday is no dateTime");
assert_eq!(error.kind, DecodeErrorKind::BadValue);
assert_eq!(error.path, "CodeSystem.date");
# Ok::<(), Box<dyn std::error::Error>>(())
```

Each form is an XML Schema pattern, so `\s` is the space, the tab, the carriage
return and the line feed alone (<https://www.w3.org/TR/xmlschema-2/#regexs>) and
a value carrying a non-breaking space, which the HL7 packages themselves
publish inside a `display` and a `code`, is not refused for it.

The forms differ between versions and are read per version, never copied:
3.0.2, 4.0.1, 4.3.0 and 6.0.0-ballot5 require a timezone offset once a
`dateTime` carries a time, and 5.0.0 makes it optional. 3.0.2 states its forms
in the `structuredefinition-regex` extension and publishes none for
`base64Binary`, `markdown`, `string` or `uri`, so the STU3 module holds those
to no form. A primitive carried as a JSON number or a boolean (`integer`, `decimal`, `positiveInt`, `unsignedInt`, `boolean`) is
checked by the parser of that scalar instead, which is what keeps a decimal's
lexical form intact.

## JSON and decimal precision

The codec reads and writes FHIR JSON through its own document model,
`fhir_types::codec::Value`, which holds a number in the text the document
carried. FHIR says "Do not use an IEEE type floating point type, instead use
something that works like a true decimal"
(<https://hl7.org/fhir/R4B/datatypes.html#decimal>), so `1.10` keeps its
trailing zero and a decimal past `f64` precision keeps its digits, through the
`Serialize` impls and through `Json::to_json` alike.

That costs a dependent nothing. The crate turns on no `serde_json` feature that
changes how numbers deserialize, so your own types keep reading and writing the
JSON they always did. Reading a document uses `serde_json`'s raw value, so the
`Deserialize` impls read `serde_json`'s format.

### Converting to and from `serde_json::Value`

A decimal is never silently altered, so the conversion out is fallible.
`Value::into_serde_json` and `Value::to_serde_json` hand the document to
`serde_json` only when every number comes back in the text the document
carried; a number `serde_json` would write differently, or cannot hold at all,
is a `ValueConversionError` naming the element path
(`Observation.valueQuantity.value`), the lexical form and what `serde_json`
writes. With `arbitrary_precision` on your side every form converts; without
it, whole numbers and the decimals `f64` writes back convert and `1.50` is
refused.

`Value::from_serde_json` is the other direction and cannot fail. It takes the
number in the text `serde_json` holds, which is the document's own form under
`arbitrary_precision` and the `f64` rendering of it otherwise, so read a
document whose decimals must stay exact through this crate's decoder
(`serde_json::from_str::<fhir_types::codec::Value>`) rather than through
`serde_json::Value`.

## Where it sits

`fhir-types` is generated in [FerroBRIDGE](https://github.com/FerroHEALTH/FerroBRIDGE),
a pure-Rust bridge between openEHR and HL7 FHIR and the OMOP Common Data Model.
The crate is published so other projects can reuse it; the API is pre-1.0 and
moves with the FerroBRIDGE release train. Documentation:
<https://docs.rs/fhir-types>.

## Licence

Apache License, Version 2.0 (`LICENSE`). This is the one first-party crate the
workspace publishes under Apache 2.0 rather than its Business Source License
1.1, so any Rust project can depend on it without a licence conversation. Clinical terminology content (SNOMED
CT, LOINC, RxNorm, ICD, the Dutch national code systems) is licensed by its
publisher and is never part of this crate.