fhir-types 0.1.113

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 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.

## 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.