Skip to main content

fhir_core/
coded.rs

1//! A coded value that is usually a known enum variant but tolerates any code.
2//!
3//! FHIR elements with a `required` binding draw their value from a value set, so
4//! this crate types them as the matching release-specific `codes` enum
5//! ([`r4::codes`](crate::r4::codes), [`r5::codes`](crate::r5::codes)). But real
6//! data occasionally carries a code outside the set (a newer code, a local
7//! extension, or simply invalid data), and a data-exchange library must not fail
8//! to parse it. [`Coded<E>`] wraps the enum with an [`Unknown`](Coded::Unknown)
9//! fallback so every wire value round-trips. See `spec/05-code-systems.md`.
10//!
11//! The wrapper itself is release-independent — it is generic over the enum — so
12//! it is defined once here and re-exported as [`r4::coded`](crate::r4::coded)
13//! and [`r5::coded`](crate::r5::coded).
14//!
15//! ```
16//! use fhir::coded::Coded;
17//!
18//! // Stands in for a generated `codes` enum such as `AdministrativeGender`.
19//! #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
20//! enum Gender {
21//!     #[serde(rename = "female")]
22//!     Female,
23//! }
24//!
25//! // A known code parses into the enum variant.
26//! let known: Coded<Gender> =
27//!     serde_json::from_value(serde_json::json!("female")).unwrap();
28//! assert_eq!(known, Coded::Known(Gender::Female));
29//!
30//! // An unrecognized code is preserved as-is rather than rejected.
31//! let unknown: Coded<Gender> =
32//!     serde_json::from_value(serde_json::json!("robot")).unwrap();
33//! assert_eq!(unknown, Coded::Unknown("robot".to_string()));
34//!
35//! // Both round-trip to their original code string.
36//! assert_eq!(serde_json::to_value(&known).unwrap(), "female");
37//! assert_eq!(serde_json::to_value(&unknown).unwrap(), "robot");
38//! ```
39
40use ::serde::{Deserialize, Serialize};
41
42use crate::validate::{Validate, ValidationIssue};
43
44/// A coded value: a known `codes` enum variant `E`, or any other code string
45/// preserved verbatim.
46///
47/// Deserialization tries `E` first (untagged) and falls back to
48/// [`Unknown`](Self::Unknown); serialization emits the underlying code string in
49/// either case.
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(untagged)]
52pub enum Coded<E> {
53    /// A recognized code from the bound value set.
54    Known(E),
55    /// A code outside the value set, preserved for round-tripping.
56    Unknown(String),
57}
58
59impl<E: Default> Default for Coded<E> {
60    fn default() -> Self {
61        Coded::Known(E::default())
62    }
63}
64
65impl<E> Coded<E> {
66    /// The known enum variant, if this value is recognized.
67    pub fn known(&self) -> Option<&E> {
68        match self {
69            Coded::Known(e) => Some(e),
70            Coded::Unknown(_) => None,
71        }
72    }
73
74    /// Whether this value is an unrecognized code.
75    #[must_use]
76    pub fn is_unknown(&self) -> bool {
77        matches!(self, Coded::Unknown(_))
78    }
79}
80
81impl<E: Serialize> Coded<E> {
82    /// The underlying FHIR code string (the enum's canonical code, or the raw
83    /// unknown code).
84    #[must_use]
85    pub fn code(&self) -> String {
86        match self {
87            Coded::Known(e) => ::serde_json::to_value(e)
88                .ok()
89                .and_then(|v| v.as_str().map(str::to_string))
90                .unwrap_or_default(),
91            Coded::Unknown(s) => s.clone(),
92        }
93    }
94}
95
96impl<E> Validate for Coded<E> {
97    // Every `Coded` field has a `required` binding (that is why it was typed as
98    // an enum), so an `Unknown` code is outside the value set (T13).
99    fn validate(&self) -> Vec<ValidationIssue> {
100        match self {
101            Coded::Known(_) => Vec::new(),
102            Coded::Unknown(code) => vec![ValidationIssue::new(
103                "code",
104                format!("code {code:?} is not in the required value set"),
105            )],
106        }
107    }
108}