Skip to main content

fml_model/
model.rs

1//! The StructureMap resource, as FHIR R5 and the Mapping Language IG define it.
2//!
3//! Fields follow the FHIR element order, so serialized maps read like the ones the HL7 tools
4//! produce. Elements this model does not name are kept in `other` maps, so reading and writing a
5//! StructureMap does not lose them.
6
7use serde::de::{self, Deserializer, MapAccess, Visitor};
8use serde::ser::{SerializeMap, Serializer};
9use serde::{Deserialize, Serialize};
10use serde_json::{Map, Value};
11
12/// A StructureMap resource.
13#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct StructureMap {
16    #[serde(default = "resource_type")]
17    pub resource_type: String,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub id: Option<String>,
20    /// Embedded resources: the concept maps declared with `conceptmap`.
21    #[serde(default, skip_serializing_if = "Vec::is_empty")]
22    pub contained: Vec<Value>,
23    #[serde(default, skip_serializing_if = "Vec::is_empty")]
24    pub extension: Vec<Extension>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub url: Option<String>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub version: Option<String>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub name: Option<String>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub title: Option<String>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub status: Option<String>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub experimental: Option<bool>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub date: Option<String>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub publisher: Option<String>,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub description: Option<String>,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub purpose: Option<String>,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub copyright: Option<String>,
47    #[serde(default, skip_serializing_if = "Vec::is_empty")]
48    pub structure: Vec<Structure>,
49    #[serde(default, skip_serializing_if = "Vec::is_empty")]
50    pub import: Vec<String>,
51    #[serde(rename = "const", default, skip_serializing_if = "Vec::is_empty")]
52    pub constants: Vec<Constant>,
53    #[serde(default, skip_serializing_if = "Vec::is_empty")]
54    pub group: Vec<Group>,
55    /// Elements not modelled above, such as `identifier`, `contact` or `jurisdiction`.
56    #[serde(flatten)]
57    pub other: Map<String, Value>,
58}
59
60fn resource_type() -> String {
61    "StructureMap".to_owned()
62}
63
64impl StructureMap {
65    pub fn new() -> Self {
66        Self {
67            resource_type: resource_type(),
68            ..Self::default()
69        }
70    }
71}
72
73/// An extension with a string value, the only kind the mapping language produces.
74#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct Extension {
77    pub url: String,
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub value_string: Option<String>,
80    #[serde(flatten)]
81    pub other: Map<String, Value>,
82}
83
84impl Extension {
85    pub fn string(url: &str, value: impl Into<String>) -> Self {
86        Self {
87            url: url.to_owned(),
88            value_string: Some(value.into()),
89            other: Map::new(),
90        }
91    }
92}
93
94/// `uses "url" alias Name as mode`
95#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
96#[serde(rename_all = "camelCase")]
97pub struct Structure {
98    pub url: String,
99    pub mode: String,
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub alias: Option<String>,
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub documentation: Option<String>,
104    #[serde(flatten)]
105    pub other: Map<String, Value>,
106}
107
108/// `let name = expression;`
109#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
110#[serde(rename_all = "camelCase")]
111pub struct Constant {
112    pub name: String,
113    pub value: String,
114    #[serde(flatten)]
115    pub other: Map<String, Value>,
116}
117
118#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
119#[serde(rename_all = "camelCase")]
120pub struct Group {
121    pub name: String,
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub extends: Option<String>,
124    /// `types` or `type-and-types`; R4 maps also use `none`.
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub type_mode: Option<String>,
127    #[serde(skip_serializing_if = "Option::is_none")]
128    pub documentation: Option<String>,
129    #[serde(default, skip_serializing_if = "Vec::is_empty")]
130    pub input: Vec<Input>,
131    #[serde(default, skip_serializing_if = "Vec::is_empty")]
132    pub rule: Vec<Rule>,
133    #[serde(flatten)]
134    pub other: Map<String, Value>,
135}
136
137#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
138#[serde(rename_all = "camelCase")]
139pub struct Input {
140    pub name: String,
141    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
142    pub type_name: Option<String>,
143    /// `source` or `target`.
144    pub mode: String,
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub documentation: Option<String>,
147    #[serde(flatten)]
148    pub other: Map<String, Value>,
149}
150
151#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
152#[serde(rename_all = "camelCase")]
153pub struct Rule {
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub name: Option<String>,
156    #[serde(default, skip_serializing_if = "Vec::is_empty")]
157    pub source: Vec<Source>,
158    #[serde(default, skip_serializing_if = "Vec::is_empty")]
159    pub target: Vec<Target>,
160    #[serde(default, skip_serializing_if = "Vec::is_empty")]
161    pub rule: Vec<Rule>,
162    #[serde(default, skip_serializing_if = "Vec::is_empty")]
163    pub dependent: Vec<Dependent>,
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub documentation: Option<String>,
166    #[serde(flatten)]
167    pub other: Map<String, Value>,
168}
169
170#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
171#[serde(rename_all = "camelCase")]
172pub struct Source {
173    /// Path segments after the first element, in the IG's element extension.
174    #[serde(default, skip_serializing_if = "Vec::is_empty")]
175    pub extension: Vec<Extension>,
176    pub context: String,
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub min: Option<i64>,
179    #[serde(skip_serializing_if = "Option::is_none")]
180    pub max: Option<String>,
181    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
182    pub type_name: Option<String>,
183    /// A FHIRPath expression.
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub default_value: Option<String>,
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub element: Option<String>,
188    #[serde(skip_serializing_if = "Option::is_none")]
189    pub list_mode: Option<String>,
190    #[serde(skip_serializing_if = "Option::is_none")]
191    pub variable: Option<String>,
192    #[serde(skip_serializing_if = "Option::is_none")]
193    pub condition: Option<String>,
194    #[serde(skip_serializing_if = "Option::is_none")]
195    pub check: Option<String>,
196    #[serde(skip_serializing_if = "Option::is_none")]
197    pub log_message: Option<String>,
198    #[serde(flatten)]
199    pub other: Map<String, Value>,
200}
201
202#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
203#[serde(rename_all = "camelCase")]
204pub struct Target {
205    #[serde(default, skip_serializing_if = "Vec::is_empty")]
206    pub extension: Vec<Extension>,
207    #[serde(skip_serializing_if = "Option::is_none")]
208    pub context: Option<String>,
209    /// R4 only: `variable` (or `type`, which R5 dropped).
210    #[serde(skip_serializing_if = "Option::is_none")]
211    pub context_type: Option<String>,
212    #[serde(skip_serializing_if = "Option::is_none")]
213    pub element: Option<String>,
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub variable: Option<String>,
216    #[serde(default, skip_serializing_if = "Vec::is_empty")]
217    pub list_mode: Vec<String>,
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub list_rule_id: Option<String>,
220    #[serde(skip_serializing_if = "Option::is_none")]
221    pub transform: Option<String>,
222    #[serde(default, skip_serializing_if = "Vec::is_empty")]
223    pub parameter: Vec<Parameter>,
224    #[serde(flatten)]
225    pub other: Map<String, Value>,
226}
227
228/// `then Group(a, b)`
229#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
230#[serde(rename_all = "camelCase")]
231pub struct Dependent {
232    pub name: String,
233    #[serde(default, skip_serializing_if = "Vec::is_empty")]
234    pub parameter: Vec<Parameter>,
235    #[serde(flatten)]
236    pub other: Map<String, Value>,
237}
238
239/// A transform or dependent-group parameter: `value[x]`, and the `_value[x]` element that carries
240/// extensions on the value (such as the reference to a contained ConceptMap).
241#[derive(Debug, Clone, PartialEq)]
242pub struct Parameter {
243    pub value: ParameterValue,
244    pub value_element: Option<Value>,
245}
246
247impl From<ParameterValue> for Parameter {
248    fn from(value: ParameterValue) -> Self {
249        Self {
250            value,
251            value_element: None,
252        }
253    }
254}
255
256/// The value of a parameter.
257#[derive(Debug, Clone, PartialEq)]
258pub enum ParameterValue {
259    /// A variable name.
260    Id(String),
261    String(String),
262    Boolean(bool),
263    Integer(i64),
264    Decimal(serde_json::Number),
265    Date(String),
266    Time(String),
267    DateTime(String),
268}
269
270impl ParameterValue {
271    fn key(&self) -> &'static str {
272        match self {
273            ParameterValue::Id(_) => "valueId",
274            ParameterValue::String(_) => "valueString",
275            ParameterValue::Boolean(_) => "valueBoolean",
276            ParameterValue::Integer(_) => "valueInteger",
277            ParameterValue::Decimal(_) => "valueDecimal",
278            ParameterValue::Date(_) => "valueDate",
279            ParameterValue::Time(_) => "valueTime",
280            ParameterValue::DateTime(_) => "valueDateTime",
281        }
282    }
283
284    fn json(&self) -> Value {
285        match self {
286            ParameterValue::Id(v)
287            | ParameterValue::String(v)
288            | ParameterValue::Date(v)
289            | ParameterValue::Time(v)
290            | ParameterValue::DateTime(v) => Value::String(v.clone()),
291            ParameterValue::Boolean(v) => Value::Bool(*v),
292            ParameterValue::Integer(v) => Value::from(*v),
293            ParameterValue::Decimal(v) => Value::Number(v.clone()),
294        }
295    }
296
297    fn from_json(key: &str, value: Value) -> Result<Option<Self>, String> {
298        let text = || {
299            value
300                .as_str()
301                .map(str::to_owned)
302                .ok_or_else(|| format!("{key} must be a string"))
303        };
304        Ok(Some(match key {
305            "valueId" => ParameterValue::Id(text()?),
306            "valueString" => ParameterValue::String(text()?),
307            "valueDate" => ParameterValue::Date(text()?),
308            "valueTime" => ParameterValue::Time(text()?),
309            "valueDateTime" => ParameterValue::DateTime(text()?),
310            "valueBoolean" => {
311                ParameterValue::Boolean(value.as_bool().ok_or("valueBoolean must be a boolean")?)
312            }
313            "valueInteger" => {
314                ParameterValue::Integer(value.as_i64().ok_or("valueInteger must be an integer")?)
315            }
316            "valueDecimal" => match value {
317                Value::Number(n) => ParameterValue::Decimal(n),
318                _ => return Err("valueDecimal must be a number".to_owned()),
319            },
320            _ => return Ok(None),
321        }))
322    }
323}
324
325impl Serialize for Parameter {
326    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
327        let key = self.value.key();
328        let mut map = serializer.serialize_map(None)?;
329        map.serialize_entry(key, &self.value.json())?;
330        if let Some(element) = &self.value_element {
331            map.serialize_entry(&format!("_{key}"), element)?;
332        }
333        map.end()
334    }
335}
336
337impl<'de> Deserialize<'de> for Parameter {
338    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
339        struct ParameterVisitor;
340
341        impl<'de> Visitor<'de> for ParameterVisitor {
342            type Value = Parameter;
343
344            fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
345                f.write_str("a StructureMap parameter with one value[x] element")
346            }
347
348            fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Parameter, A::Error> {
349                let mut value = None;
350                let mut value_element = None;
351                while let Some(key) = map.next_key::<String>()? {
352                    let json: Value = map.next_value()?;
353                    if key.starts_with("_value") {
354                        value_element = Some(json);
355                    } else if key.starts_with("value") {
356                        value = Some(
357                            ParameterValue::from_json(&key, json)
358                                .map_err(de::Error::custom)?
359                                .ok_or_else(|| {
360                                    de::Error::custom(format!("unsupported parameter type {key}"))
361                                })?,
362                        );
363                    }
364                }
365                let value = value.ok_or_else(|| de::Error::custom("parameter has no value[x]"))?;
366                Ok(Parameter {
367                    value,
368                    value_element,
369                })
370            }
371        }
372
373        deserializer.deserialize_map(ParameterVisitor)
374    }
375}