Skip to main content

haste_fhir_model/r4/
conversion.rs

1use crate::r4::{
2    datetime::{Date, DateTime, Instant, Time},
3    generated::types::{FHIRBoolean, FHIRDecimal, FHIRInteger, FHIRPositiveInt, FHIRUnsignedInt},
4};
5use haste_reflect::MetaValue;
6use std::{collections::HashSet, sync::LazyLock};
7use thiserror::Error;
8
9#[derive(Error, Debug)]
10pub enum DowncastError {
11    #[error("Failed to downcast value to type '{0}'")]
12    FailedDowncast(String),
13}
14
15/// Number types to use in FHIR evaluation
16pub static NUMBER_TYPES: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
17    let mut m = HashSet::new();
18    m.insert("integer");
19    m.insert("decimal");
20    m.insert("positiveInt");
21    m.insert("unsignedInt");
22    m.insert("http://hl7.org/fhirpath/System.Decimal");
23    m.insert("http://hl7.org/fhirpath/System.Integer");
24    m
25});
26
27pub static BOOLEAN_TYPES: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
28    let mut m = HashSet::new();
29    m.insert("boolean");
30    m.insert("http://hl7.org/fhirpath/System.Boolean");
31    m
32});
33
34pub static DATE_TIME_TYPES: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
35    let mut m = HashSet::new();
36    m.insert("date");
37    m.insert("dateTime");
38    m.insert("instant");
39    m.insert("time");
40    m.insert("http://hl7.org/fhirpath/System.DateTime");
41    m.insert("http://hl7.org/fhirpath/System.Instant");
42    m.insert("http://hl7.org/fhirpath/System.Date");
43    m.insert("http://hl7.org/fhirpath/System.Time");
44    m
45});
46
47pub static STRING_TYPES: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
48    let mut m = HashSet::new();
49    m.insert("base64Binary");
50    m.insert("canonical");
51
52    m.insert("id");
53    m.insert("code");
54    m.insert("string");
55    m.insert("oid");
56    m.insert("uri");
57    m.insert("url");
58    m.insert("uuid");
59    m.insert("xhtml");
60
61    m.insert("http://hl7.org/fhirpath/System.String");
62    m
63});
64
65pub static PRIMITIVE_TYPES: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
66    let mut res = BOOLEAN_TYPES.clone();
67    res.extend(NUMBER_TYPES.iter().map(|s| *s));
68    res.extend(DATE_TIME_TYPES.iter().map(|s| *s));
69    res.extend(STRING_TYPES.iter().map(|s| *s));
70
71    res
72});
73
74pub fn downcast_bool(value: &dyn MetaValue) -> Result<bool, DowncastError> {
75    match value.fhir_type() {
76        "http://hl7.org/fhirpath/System.Boolean" => value
77            .as_any()
78            .downcast_ref::<bool>()
79            .map(|v| *v)
80            .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string())),
81        "boolean" => {
82            let fp_bool = value
83                .as_any()
84                .downcast_ref::<FHIRBoolean>()
85                .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string()))?;
86            downcast_bool(fp_bool.value.as_ref().unwrap_or(&false))
87        }
88        type_name => Err(DowncastError::FailedDowncast(type_name.to_string())),
89    }
90}
91
92pub fn downcast_string(value: &dyn MetaValue) -> Result<String, DowncastError> {
93    match value.fhir_type() {
94        "canonical" | "base64Binary" | "code" | "string" | "oid" | "uri" | "url" | "uuid"
95        | "id" | "xhtml" => downcast_string(value.get_field("value").unwrap_or(&"".to_string())),
96
97        "http://hl7.org/fhirpath/System.String" => value
98            .as_any()
99            .downcast_ref::<String>()
100            .map(|v| v.clone())
101            .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string())),
102
103        type_name => Err(DowncastError::FailedDowncast(type_name.to_string())),
104    }
105}
106
107pub fn downcast_number(value: &dyn MetaValue) -> Result<f64, DowncastError> {
108    match value.fhir_type() {
109        "integer" => {
110            let fp_integer = value
111                .as_any()
112                .downcast_ref::<FHIRInteger>()
113                .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string()))?;
114            downcast_number(fp_integer.value.as_ref().unwrap_or(&0))
115        }
116        "decimal" => {
117            let fp_decimal = value
118                .as_any()
119                .downcast_ref::<FHIRDecimal>()
120                .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string()))?;
121            downcast_number(fp_decimal.value.as_ref().unwrap_or(&0.0))
122        }
123        "positiveInt" => {
124            let fp_positive_int = value
125                .as_any()
126                .downcast_ref::<FHIRPositiveInt>()
127                .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string()))?;
128
129            downcast_number(fp_positive_int.value.as_ref().unwrap_or(&0))
130        }
131        "unsignedInt" => {
132            let fp_unsigned_int = value
133                .as_any()
134                .downcast_ref::<FHIRUnsignedInt>()
135                .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string()))?;
136
137            downcast_number(fp_unsigned_int.value.as_ref().unwrap_or(&0))
138        }
139        "http://hl7.org/fhirpath/System.Integer" => value
140            .as_any()
141            .downcast_ref::<i64>()
142            .map(|v| *v as f64)
143            .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string())),
144
145        "http://hl7.org/fhirpath/System.Decimal" => value
146            .as_any()
147            .downcast_ref::<f64>()
148            .map(|v| *v)
149            .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string())),
150        type_name => Err(DowncastError::FailedDowncast(type_name.to_string())),
151    }
152}
153
154pub fn downcast_datetime(value: &dyn MetaValue) -> Result<String, DowncastError> {
155    // For simplicity, we will just downcast to string for date and datetime types, as FHIRPath evaluation only requires string representation of dates.
156    match value.fhir_type() {
157        "date" | "dateTime" | "instant" | "time" => {
158            downcast_datetime(value.get_field("value").unwrap_or(&"".to_string()))
159        }
160        "http://hl7.org/fhirpath/System.Date" => {
161            let fp_date = value
162                .as_any()
163                .downcast_ref::<Date>()
164                .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string()))?;
165
166            Ok(fp_date.to_string())
167        }
168        "http://hl7.org/fhirpath/System.DateTime" => {
169            let fp_datetime = value
170                .as_any()
171                .downcast_ref::<DateTime>()
172                .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string()))?;
173
174            Ok(fp_datetime.to_string())
175        }
176        "http://hl7.org/fhirpath/System.Instant" => {
177            let fp_instant = value
178                .as_any()
179                .downcast_ref::<Instant>()
180                .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string()))?;
181
182            Ok(fp_instant.to_string())
183        }
184        "http://hl7.org/fhirpath/System.Time" => {
185            let fp_time = value
186                .as_any()
187                .downcast_ref::<Time>()
188                .ok_or_else(|| DowncastError::FailedDowncast(value.fhir_type().to_string()))?;
189            Ok(fp_time.to_string())
190        }
191        type_name => Err(DowncastError::FailedDowncast(type_name.to_string())),
192    }
193}