Skip to main content

fhir_core/
primvec.rs

1//! The value array of a repeating FHIR primitive (audit **F-86**).
2//!
3//! FHIR JSON represents a repeating primitive as **parallel arrays**: the
4//! value array, and an `_element` array carrying each position's
5//! `id`/`extension`. A position that carries only an extension is a **null**
6//! in the value array:
7//!
8//! ```json
9//! { "event": [null], "_event": [{ "extension": [ … ] }] }
10//! ```
11//!
12//! That is valid FHIR — HL7's own R4B examples use it — and `Vec<T>` cannot
13//! hold it: there is no way to represent "no value at this position". Until
14//! 2026-08-10 the model rejected the null outright (and, before **F-87**'s
15//! fix the same day, then silently dropped the surrounding element).
16//! [`PrimVec`] is the value array as the wire defines it: a sequence of
17//! positions, each a value or an extension-only placeholder.
18
19use serde::{Deserialize, Serialize};
20
21use crate::validate::{Validate, ValidationIssue};
22
23/// The values of a repeating FHIR primitive element (`0..*`).
24///
25/// A thin, transparent wrapper over `Vec<Option<T>>`: `None` is an
26/// extension-only placeholder — the JSON `null` whose position in the
27/// paired `_element` array carries the extension. Use [`values`] to iterate
28/// the actual values, [`iter`] to see positions.
29///
30/// A placeholder with no corresponding `_element` entry is meaningless in
31/// FHIR; nothing here prevents constructing one, but the serializer will
32/// faithfully write the `null`, and validation flags it
33/// ([`Validate for PrimVec`](#impl-Validate-for-PrimVec<T>)).
34///
35/// [`values`]: PrimVec::values
36/// [`iter`]: PrimVec::iter
37#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
38#[serde(transparent)]
39pub struct PrimVec<T>(pub Vec<Option<T>>);
40
41impl<T> PrimVec<T> {
42    /// An empty value array.
43    #[must_use]
44    pub fn new() -> Self {
45        Self(Vec::new())
46    }
47
48    /// Number of positions, placeholders included.
49    #[must_use]
50    pub fn len(&self) -> usize {
51        self.0.len()
52    }
53
54    /// True when there are no positions at all.
55    #[must_use]
56    pub fn is_empty(&self) -> bool {
57        self.0.is_empty()
58    }
59
60    /// The values, skipping extension-only placeholders.
61    pub fn values(&self) -> impl Iterator<Item = &T> {
62        self.0.iter().filter_map(Option::as_ref)
63    }
64
65    /// Every position: `Some(value)` or `None` for a placeholder.
66    pub fn iter(&self) -> std::slice::Iter<'_, Option<T>> {
67        self.0.iter()
68    }
69
70    /// Append a value.
71    pub fn push(&mut self, value: T) {
72        self.0.push(Some(value));
73    }
74
75    /// Append an extension-only placeholder (a JSON `null`; its extension
76    /// lives at the same index of the `_element` sibling field).
77    pub fn push_placeholder(&mut self) {
78        self.0.push(None);
79    }
80
81    /// The first actual value, if any position holds one.
82    #[must_use]
83    pub fn first_value(&self) -> Option<&T> {
84        self.values().next()
85    }
86}
87
88impl<T> From<Vec<T>> for PrimVec<T> {
89    fn from(values: Vec<T>) -> Self {
90        Self(values.into_iter().map(Some).collect())
91    }
92}
93
94impl<T> FromIterator<T> for PrimVec<T> {
95    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
96        Self(iter.into_iter().map(Some).collect())
97    }
98}
99
100impl<T> FromIterator<Option<T>> for PrimVec<T> {
101    fn from_iter<I: IntoIterator<Item = Option<T>>>(iter: I) -> Self {
102        Self(iter.into_iter().collect())
103    }
104}
105
106impl<T> IntoIterator for PrimVec<T> {
107    type Item = Option<T>;
108    type IntoIter = std::vec::IntoIter<Option<T>>;
109    fn into_iter(self) -> Self::IntoIter {
110        self.0.into_iter()
111    }
112}
113
114impl<'a, T> IntoIterator for &'a PrimVec<T> {
115    type Item = &'a Option<T>;
116    type IntoIter = std::slice::Iter<'a, Option<T>>;
117    fn into_iter(self) -> Self::IntoIter {
118        self.0.iter()
119    }
120}
121
122impl<T: Validate> Validate for PrimVec<T> {
123    fn validate(&self) -> Vec<ValidationIssue> {
124        let mut issues = Vec::new();
125        for v in self.values() {
126            issues.extend(v.validate());
127        }
128        issues
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn null_positions_round_trip() {
138        // The F-86 shape: a value, then an extension-only placeholder.
139        let json = ::serde_json::json!(["a", null]);
140        let v: PrimVec<String> = ::serde_json::from_value(json.clone()).expect("nulls parse");
141        assert_eq!(v.len(), 2);
142        assert_eq!(v.values().collect::<Vec<_>>(), ["a"]);
143        assert_eq!(::serde_json::to_value(&v).expect("ser"), json);
144    }
145
146    #[test]
147    fn plain_arrays_are_unchanged() {
148        let json = ::serde_json::json!(["a", "b"]);
149        let v: PrimVec<String> = ::serde_json::from_value(json.clone()).expect("parse");
150        assert_eq!(v.values().count(), 2);
151        assert_eq!(::serde_json::to_value(&v).expect("ser"), json);
152    }
153
154    #[test]
155    fn construction_from_plain_values() {
156        let v: PrimVec<i32> = vec![1, 2].into();
157        assert_eq!(v.len(), 2);
158        let w: PrimVec<i32> = [1, 2].into_iter().collect();
159        assert_eq!(v, w);
160    }
161}