Skip to main content

haste_fhir_search/
indexing_conversion.rs

1/// Reference of conversions found here https://www.hl7.org/fhir/R4/search.html#table
2use haste_fhir_model::r4::{
3    datetime::{Date, DateTime, Instant},
4    generated::{
5        resources::{ResourceType, ResourceTypeError, SearchParameter},
6        terminology::SearchParamType,
7        types::{
8            Address, Age, CodeableConcept, Coding, ContactPoint, Duration, FHIRBoolean,
9            FHIRCanonical, FHIRDate, FHIRDateTime, FHIRDecimal, FHIRId, FHIRInstant, FHIRInteger,
10            FHIRMarkdown, FHIRPositiveInt, FHIRString, FHIRUnsignedInt, FHIRUri, FHIRUrl, FHIRUuid,
11            HumanName, Identifier, Money, Period, Quantity, Range, Reference, Timing,
12        },
13    },
14};
15use haste_fhir_operation_error::{OperationOutcomeError, derive::OperationOutcomeError};
16use haste_reflect::MetaValue;
17use serde::{Deserialize, Serialize};
18
19#[derive(Debug, Serialize, Deserialize)]
20pub struct TokenIndex {
21    system: Option<String>,
22    code: Option<String>,
23}
24
25#[derive(Debug, Serialize, Deserialize)]
26#[serde(untagged)]
27enum RangeValue {
28    Number(f64),
29    Infinity,
30}
31
32#[derive(Debug, Serialize, Deserialize)]
33pub struct QuantityRange {
34    start_value: RangeValue,
35    start_code: Option<String>,
36    start_system: Option<String>,
37    end_value: RangeValue,
38    end_code: Option<String>,
39    end_system: Option<String>,
40}
41
42#[derive(Debug, Serialize, Deserialize, PartialEq)]
43pub struct DateRange {
44    /// Milliseconds since epoch.
45    pub start: i64,
46    pub end: i64,
47}
48
49#[derive(Debug, Serialize, Deserialize)]
50pub struct ReferenceIndex {
51    id: Option<String>,
52    resource_type: Option<String>,
53    uri: Option<String>,
54}
55
56#[derive(Debug, Serialize, Deserialize)]
57#[serde(untagged)]
58pub enum InsertableIndex {
59    // Used for internal indexing only
60    Meta(String),
61    // Fhir Indexing types
62    String(Vec<String>),
63    Number(Vec<f64>),
64    URI(Vec<String>),
65    Token(Vec<TokenIndex>),
66    Date(Vec<DateRange>),
67    Reference(Vec<ReferenceIndex>),
68    Quantity(Vec<QuantityRange>),
69    Composite(Vec<String>),
70    Special(Vec<String>),
71}
72
73#[derive(OperationOutcomeError, Debug)]
74pub enum InsertableIndexError {
75    #[fatal(
76        code = "exception",
77        diagnostic = "Invalid type for insertable index: '{arg0}'"
78    )]
79    InvalidType(String),
80    #[fatal(
81        code = "exception",
82        diagnostic = "Failed to downcast value to number: {arg0}"
83    )]
84    FailedDowncast(String),
85    #[fatal(
86        code = "exception",
87        diagnostic = "Reference contains invalid resource type."
88    )]
89    ResourceTypeError(#[from] ResourceTypeError),
90}
91
92// "http://hl7.org/fhirpath/System.String" => value
93//     .as_any()
94//     .downcast_ref::<String>()
95//     .map(|v| vec![v.clone()])
96//     .ok_or_else(|| InsertableIndexError::FailedDowncast(value.typename().to_string())),
97
98fn convert_fp_string(value: &FHIRString) -> Vec<String> {
99    value
100        .value
101        .as_ref()
102        .map(|v| vec![v.to_string()])
103        .unwrap_or_else(|| vec![])
104}
105
106fn convert_optional_fp_string(value: &Option<Box<FHIRString>>) -> Vec<String> {
107    value
108        .as_ref()
109        .map(|v| convert_fp_string(v))
110        .unwrap_or_else(|| vec![])
111}
112
113fn convert_optional_fp_string_vec(value: &Option<Vec<Box<FHIRString>>>) -> Vec<String> {
114    value
115        .as_ref()
116        .map(|v| v.iter().flat_map(|s| convert_fp_string(s)).collect())
117        .unwrap_or_else(|| vec![])
118}
119
120fn index_string(value: &dyn MetaValue) -> Result<Vec<String>, InsertableIndexError> {
121    match value.typename() {
122        "FHIRString" => {
123            let fp_string = value.as_any().downcast_ref::<FHIRString>().ok_or_else(|| {
124                InsertableIndexError::FailedDowncast(value.typename().to_string())
125            })?;
126            Ok(fp_string
127                .value
128                .as_ref()
129                .map(|v| vec![v.to_string()])
130                .unwrap_or_else(|| vec![]))
131        }
132        // Even though spec states won't encounter this it does. [ImplementationGuide.description]
133        "FHIRMarkdown" => {
134            let fp_markdown = value
135                .as_any()
136                .downcast_ref::<FHIRMarkdown>()
137                .ok_or_else(|| {
138                    InsertableIndexError::FailedDowncast(value.typename().to_string())
139                })?;
140            Ok(fp_markdown
141                .value
142                .as_ref()
143                .map(|v| vec![v.to_string()])
144                .unwrap_or_else(|| vec![]))
145        }
146        "HumanName" => {
147            let human_name = value.as_any().downcast_ref::<HumanName>().ok_or_else(|| {
148                InsertableIndexError::FailedDowncast(value.typename().to_string())
149            })?;
150
151            let mut string_index = vec![];
152            string_index.extend(convert_optional_fp_string(&human_name.text));
153            string_index.extend(convert_optional_fp_string(&human_name.family));
154            string_index.extend(convert_optional_fp_string_vec(&human_name.given));
155            string_index.extend(convert_optional_fp_string_vec(&human_name.prefix));
156            string_index.extend(convert_optional_fp_string_vec(&human_name.suffix));
157            Ok(string_index)
158        }
159        "Address" => {
160            let mut string_index = vec![];
161            let address = value.as_any().downcast_ref::<Address>().ok_or_else(|| {
162                InsertableIndexError::FailedDowncast(value.typename().to_string())
163            })?;
164            string_index.extend(convert_optional_fp_string(&address.text));
165            string_index.extend(convert_optional_fp_string_vec(&address.line));
166            string_index.extend(convert_optional_fp_string(&address.city));
167            string_index.extend(convert_optional_fp_string(&address.district));
168            string_index.extend(convert_optional_fp_string(&address.state));
169            string_index.extend(convert_optional_fp_string(&address.postalCode));
170            string_index.extend(convert_optional_fp_string(&address.country));
171
172            Ok(string_index)
173        }
174
175        type_name => Err(InsertableIndexError::FailedDowncast(type_name.to_string())),
176    }
177}
178
179fn index_number(value: &dyn MetaValue) -> Result<Vec<f64>, InsertableIndexError> {
180    match value.typename() {
181        "FHIRInteger" => {
182            let fp_integer = value
183                .as_any()
184                .downcast_ref::<FHIRInteger>()
185                .ok_or_else(|| {
186                    InsertableIndexError::FailedDowncast(value.typename().to_string())
187                })?;
188            Ok(fp_integer
189                .value
190                .as_ref()
191                .map(|v| vec![*v as f64])
192                .unwrap_or_else(|| vec![]))
193        }
194        "FHIRDecimal" => {
195            let fp_decimal = value
196                .as_any()
197                .downcast_ref::<FHIRDecimal>()
198                .ok_or_else(|| {
199                    InsertableIndexError::FailedDowncast(value.typename().to_string())
200                })?;
201            Ok(fp_decimal
202                .value
203                .as_ref()
204                .map(|v| vec![*v as f64])
205                .unwrap_or_else(|| vec![]))
206        }
207        "FHIRPositiveInt" => {
208            let fp_positive_int = value
209                .as_any()
210                .downcast_ref::<FHIRPositiveInt>()
211                .ok_or_else(|| {
212                    InsertableIndexError::FailedDowncast(value.typename().to_string())
213                })?;
214
215            Ok(fp_positive_int
216                .value
217                .as_ref()
218                .map(|v| vec![*v as f64])
219                .unwrap_or_else(|| vec![]))
220        }
221        "FHIRUnsignedInt" => {
222            let fp_unsigned_int = value
223                .as_any()
224                .downcast_ref::<FHIRUnsignedInt>()
225                .ok_or_else(|| {
226                    InsertableIndexError::FailedDowncast(value.typename().to_string())
227                })?;
228
229            Ok(fp_unsigned_int
230                .value
231                .as_ref()
232                .map(|v| vec![*v as f64])
233                .unwrap_or_else(|| vec![]))
234        }
235        type_name => Err(InsertableIndexError::FailedDowncast(type_name.to_string())),
236    }
237}
238
239fn index_uri(value: &dyn MetaValue) -> Result<Vec<String>, InsertableIndexError> {
240    match value.typename() {
241        "FHIRUrl" => {
242            let fp_uri = value.as_any().downcast_ref::<FHIRUrl>().ok_or_else(|| {
243                InsertableIndexError::FailedDowncast(value.typename().to_string())
244            })?;
245            Ok(fp_uri
246                .value
247                .as_ref()
248                .map(|v| vec![v.to_string()])
249                .unwrap_or_else(|| vec![]))
250        }
251        "FHIRUuid" => {
252            let fp_uri = value.as_any().downcast_ref::<FHIRUuid>().ok_or_else(|| {
253                InsertableIndexError::FailedDowncast(value.typename().to_string())
254            })?;
255            Ok(fp_uri
256                .value
257                .as_ref()
258                .map(|v| vec![v.to_string()])
259                .unwrap_or_else(|| vec![]))
260        }
261        "FHIRCanonical" => {
262            let fp_uri = value
263                .as_any()
264                .downcast_ref::<FHIRCanonical>()
265                .ok_or_else(|| {
266                    InsertableIndexError::FailedDowncast(value.typename().to_string())
267                })?;
268            Ok(fp_uri
269                .value
270                .as_ref()
271                .map(|v| vec![v.to_string()])
272                .unwrap_or_else(|| vec![]))
273        }
274        "FHIRUri" => {
275            let fp_uri = value.as_any().downcast_ref::<FHIRUri>().ok_or_else(|| {
276                InsertableIndexError::FailedDowncast(value.typename().to_string())
277            })?;
278            Ok(fp_uri
279                .value
280                .as_ref()
281                .map(|v| vec![v.to_string()])
282                .unwrap_or_else(|| vec![]))
283        }
284        type_name => Err(InsertableIndexError::FailedDowncast(type_name.to_string())),
285    }
286}
287
288fn index_token(value: &dyn MetaValue) -> Result<Vec<TokenIndex>, InsertableIndexError> {
289    match value.typename() {
290        "Coding" => {
291            let fp_coding = value.as_any().downcast_ref::<Coding>().ok_or_else(|| {
292                InsertableIndexError::FailedDowncast(value.typename().to_string())
293            })?;
294
295            Ok(vec![TokenIndex {
296                system: fp_coding.system.as_ref().and_then(|s| s.value.clone()),
297                code: fp_coding.code.as_ref().and_then(|v| v.value.clone()),
298            }])
299        }
300        "CodeableConcept" => {
301            let fp_codeable_concept = value
302                .as_any()
303                .downcast_ref::<CodeableConcept>()
304                .ok_or_else(|| {
305                    InsertableIndexError::FailedDowncast(value.typename().to_string())
306                })?;
307
308            Ok(fp_codeable_concept
309                .coding
310                .as_ref()
311                .and_then(|coding| {
312                    Some(
313                        coding
314                            .iter()
315                            .map(|c| TokenIndex {
316                                system: c.system.as_ref().and_then(|s| s.value.clone()),
317                                code: c.code.as_ref().and_then(|v| v.value.clone()),
318                            })
319                            .collect::<Vec<_>>(),
320                    )
321                })
322                .unwrap_or_else(|| vec![]))
323        }
324        "Identifier" => {
325            let fp_identifier = value.as_any().downcast_ref::<Identifier>().ok_or_else(|| {
326                InsertableIndexError::FailedDowncast(value.typename().to_string())
327            })?;
328
329            Ok(vec![TokenIndex {
330                system: fp_identifier.system.as_ref().and_then(|s| s.value.clone()),
331                code: fp_identifier.value.as_ref().and_then(|v| v.value.clone()),
332            }])
333        }
334        "ContactPoint" => {
335            let fp_contact_point =
336                value
337                    .as_any()
338                    .downcast_ref::<ContactPoint>()
339                    .ok_or_else(|| {
340                        InsertableIndexError::FailedDowncast(value.typename().to_string())
341                    })?;
342
343            Ok(vec![TokenIndex {
344                system: None,
345                code: fp_contact_point
346                    .value
347                    .as_ref()
348                    .and_then(|v| v.value.clone()),
349            }])
350        }
351        "FHIRCode" => {
352            let fp_code = value
353                .get_field("value")
354                .and_then(|v| v.as_any().downcast_ref::<String>());
355
356            Ok(vec![TokenIndex {
357                system: None,
358                code: fp_code.map(|v| v.to_string()),
359            }])
360        }
361        "FHIRBoolean" => {
362            let fp_boolean = value
363                .as_any()
364                .downcast_ref::<FHIRBoolean>()
365                .ok_or_else(|| {
366                    InsertableIndexError::FailedDowncast(value.typename().to_string())
367                })?;
368
369            Ok(vec![TokenIndex {
370                system: Some("http://hl7.org/fhir/special-values".to_string()),
371                code: fp_boolean.value.as_ref().map(|v| v.to_string()),
372            }])
373        }
374        "http://hl7.org/fhirpath/System.String" => {
375            let string = value.as_any().downcast_ref::<String>().ok_or_else(|| {
376                InsertableIndexError::FailedDowncast(value.typename().to_string())
377            })?;
378
379            Ok(vec![TokenIndex {
380                system: None,
381                code: Some(string.clone()),
382            }])
383        }
384        "FHIRString" => {
385            let fp_string = value.as_any().downcast_ref::<FHIRString>().ok_or_else(|| {
386                InsertableIndexError::FailedDowncast(value.typename().to_string())
387            })?;
388
389            Ok(vec![TokenIndex {
390                system: None,
391                code: fp_string.value.as_ref().map(|v| v.to_string()),
392            }])
393        }
394        "FHIRId" => {
395            let fp_id = value.as_any().downcast_ref::<FHIRId>().ok_or_else(|| {
396                InsertableIndexError::FailedDowncast(value.typename().to_string())
397            })?;
398
399            Ok(vec![TokenIndex {
400                system: None,
401                code: fp_id.value.as_ref().map(|v| v.to_string()),
402            }])
403        }
404        _ => Err(InsertableIndexError::FailedDowncast(
405            value.typename().to_string(),
406        )),
407    }
408}
409
410fn get_decimal_precision(value: &str) -> u32 {
411    let value = value.to_string();
412    let decimal_characters = value.split('.').nth(1);
413    let mut digits = 0;
414    if let Some(decimal_part) = decimal_characters {
415        decimal_part.chars().for_each(|_| digits += 1);
416    }
417
418    digits
419}
420
421#[derive(Debug)]
422pub struct DecimalRange {
423    pub start: f64,
424    pub end: f64,
425}
426
427// Number and quantity dependent on the precision for indexing.
428pub fn get_decimal_range(value: &str) -> Result<DecimalRange, InsertableIndexError> {
429    let decimal_precision = get_decimal_precision(value);
430    let parsed_v = value
431        .parse::<f64>()
432        .map_err(|_e| InsertableIndexError::FailedDowncast(value.to_string()))?;
433
434    return Ok(DecimalRange {
435        start: parsed_v - 0.5 * 10f64.powi(-(decimal_precision as i32)),
436        end: parsed_v + 0.5 * 10f64.powi(-(decimal_precision as i32)),
437    });
438}
439
440fn fhirdecimal_to_quantity_range(value: &Option<Box<FHIRDecimal>>) -> Option<DecimalRange> {
441    let decimal_range = value.as_ref().and_then(|v| {
442        v.value
443            .as_ref()
444            .and_then(|v| get_decimal_range(&v.to_string()).ok())
445    });
446
447    decimal_range
448}
449
450fn index_quantity(value: &dyn MetaValue) -> Result<Vec<QuantityRange>, InsertableIndexError> {
451    match value.typename() {
452        "Range" => {
453            let fp_range = value.as_any().downcast_ref::<Range>().ok_or_else(|| {
454                InsertableIndexError::FailedDowncast(value.typename().to_string())
455            })?;
456            if fp_range.low.is_some() || fp_range.high.is_some() {
457                let start_value = fp_range
458                    .low
459                    .as_ref()
460                    .and_then(|v| v.value.as_ref().and_then(|v| v.value));
461                let start_system = fp_range
462                    .low
463                    .as_ref()
464                    .and_then(|v| v.system.as_ref().and_then(|s| s.value.clone()));
465                let start_code = fp_range
466                    .low
467                    .as_ref()
468                    .and_then(|v| v.code.as_ref().and_then(|c| c.value.clone()));
469
470                let end_value = fp_range
471                    .high
472                    .as_ref()
473                    .and_then(|v| v.value.as_ref().and_then(|v| v.value));
474                let end_system = fp_range
475                    .high
476                    .as_ref()
477                    .and_then(|v| v.system.as_ref().and_then(|s| s.value.clone()));
478                let end_code = fp_range
479                    .high
480                    .as_ref()
481                    .and_then(|v| v.code.as_ref().and_then(|c| c.value.clone()));
482
483                return Ok(vec![QuantityRange {
484                    start_system: start_system,
485                    start_code: start_code,
486                    start_value: start_value
487                        .map_or(RangeValue::Infinity, |v| RangeValue::Number(v)),
488                    end_system: end_system,
489                    end_code: end_code,
490                    end_value: end_value.map_or(RangeValue::Infinity, |v| RangeValue::Number(v)),
491                }]);
492            }
493            return Ok(vec![]);
494        }
495        "Age" => {
496            let fp_age = value.as_any().downcast_ref::<Age>().ok_or_else(|| {
497                InsertableIndexError::FailedDowncast(value.typename().to_string())
498            })?;
499
500            if let Some(decimal_range) = fhirdecimal_to_quantity_range(&fp_age.value) {
501                return Ok(vec![QuantityRange {
502                    start_system: fp_age.system.as_ref().and_then(|s| s.value.clone()),
503                    start_code: fp_age.code.as_ref().and_then(|c| c.value.clone()),
504                    start_value: RangeValue::Number(decimal_range.start),
505                    end_system: fp_age.system.as_ref().and_then(|s| s.value.clone()),
506                    end_code: fp_age.code.as_ref().and_then(|c| c.value.clone()),
507                    end_value: RangeValue::Number(decimal_range.end),
508                }]);
509            } else {
510                return Ok(vec![]);
511            }
512        }
513        "Money" => {
514            let fp_money = value.as_any().downcast_ref::<Money>().ok_or_else(|| {
515                InsertableIndexError::FailedDowncast(value.typename().to_string())
516            })?;
517
518            if let Some(decimal_range) = fhirdecimal_to_quantity_range(&fp_money.value) {
519                return Ok(vec![QuantityRange {
520                    start_system: Some("urn:iso:std:iso:4217".to_string()),
521                    start_code: fp_money.currency.as_ref().and_then(|c| c.value.clone()),
522                    start_value: RangeValue::Number(decimal_range.start),
523                    end_system: Some("urn:iso:std:iso:4217".to_string()),
524                    end_code: fp_money.currency.as_ref().and_then(|c| c.value.clone()),
525                    end_value: RangeValue::Number(decimal_range.end),
526                }]);
527            } else {
528                return Ok(vec![]);
529            }
530        }
531        "Duration" => {
532            let fp_duration = value.as_any().downcast_ref::<Duration>().ok_or_else(|| {
533                InsertableIndexError::FailedDowncast(value.typename().to_string())
534            })?;
535
536            if let Some(decimal_range) = fhirdecimal_to_quantity_range(&fp_duration.value) {
537                return Ok(vec![QuantityRange {
538                    start_system: fp_duration.system.as_ref().and_then(|s| s.value.clone()),
539                    start_code: fp_duration.code.as_ref().and_then(|c| c.value.clone()),
540                    start_value: RangeValue::Number(decimal_range.start),
541                    end_system: fp_duration.system.as_ref().and_then(|s| s.value.clone()),
542                    end_code: fp_duration.code.as_ref().and_then(|c| c.value.clone()),
543                    end_value: RangeValue::Number(decimal_range.end),
544                }]);
545            } else {
546                return Ok(vec![]);
547            }
548        }
549        "Quantity" => {
550            let fp_quantity = value.as_any().downcast_ref::<Quantity>().ok_or_else(|| {
551                InsertableIndexError::FailedDowncast(value.typename().to_string())
552            })?;
553
554            if let Some(decimal_range) = fhirdecimal_to_quantity_range(&fp_quantity.value) {
555                return Ok(vec![QuantityRange {
556                    start_system: fp_quantity.system.as_ref().and_then(|s| s.value.clone()),
557                    start_code: fp_quantity.code.as_ref().and_then(|c| c.value.clone()),
558                    start_value: RangeValue::Number(decimal_range.start),
559                    end_system: fp_quantity.system.as_ref().and_then(|s| s.value.clone()),
560                    end_code: fp_quantity.code.as_ref().and_then(|c| c.value.clone()),
561                    end_value: RangeValue::Number(decimal_range.end),
562                }]);
563            } else {
564                return Ok(vec![]);
565            }
566        }
567        _ => Err(InsertableIndexError::FailedDowncast(
568            value.typename().to_string(),
569        )),
570    }
571}
572
573fn year_to_daterange(year: u16) -> Result<DateRange, InsertableIndexError> {
574    let start_date = chrono::NaiveDate::from_ymd_opt(year as i32, 1, 1)
575        .and_then(|d| d.and_hms_opt(0, 0, 0))
576        .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
577
578    let end_date = chrono::NaiveDate::from_ymd_opt(year as i32 + 1, 1, 1)
579        .and_then(|d| d.pred_opt())
580        .and_then(|d| d.and_hms_milli_opt(23, 59, 59, 999))
581        .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
582
583    Ok(DateRange {
584        start: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(start_date, chrono::Utc)
585            .timestamp_millis(),
586        end: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(end_date, chrono::Utc)
587            .timestamp_millis(),
588    })
589}
590
591fn year_month_to_daterange(year: u16, month: u8) -> Result<DateRange, InsertableIndexError> {
592    let start_date = chrono::NaiveDate::from_ymd_opt(year as i32, month as u32, 1)
593        .and_then(|d| d.and_hms_opt(0, 0, 0))
594        .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
595
596    let end_date = if month < 12 {
597        chrono::NaiveDate::from_ymd_opt(year as i32, (month + 1).into(), 1)
598            .and_then(|d| d.pred_opt())
599            .and_then(|d| d.and_hms_milli_opt(23, 59, 59, 999))
600    } else {
601        chrono::NaiveDate::from_ymd_opt(year as i32 + 1, 1, 1)
602            .and_then(|d| d.pred_opt())
603            .and_then(|d| d.and_hms_milli_opt(23, 59, 59, 999))
604    }
605    .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
606
607    Ok(DateRange {
608        start: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(start_date, chrono::Utc)
609            .timestamp_millis(),
610        end: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(end_date, chrono::Utc)
611            .timestamp_millis(),
612    })
613}
614
615fn year_month_day_to_daterange(
616    year: u16,
617    month: u8,
618    day: u8,
619) -> Result<DateRange, InsertableIndexError> {
620    let start_date = chrono::NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32)
621        .and_then(|d| d.and_hms_opt(0, 0, 0))
622        .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
623
624    let end_date = chrono::NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32)
625        .and_then(|d| d.and_hms_milli_opt(23, 59, 59, 999))
626        .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
627
628    Ok(DateRange {
629        start: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(start_date, chrono::Utc)
630            .timestamp_millis(),
631        end: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(end_date, chrono::Utc)
632            .timestamp_millis(),
633    })
634}
635
636pub fn date_time_range(value: &DateTime) -> Result<DateRange, InsertableIndexError> {
637    match value {
638        DateTime::Year(year) => Ok(year_to_daterange(*year)?),
639        DateTime::YearMonth(year, month) => Ok(year_month_to_daterange(*year, *month)?),
640        DateTime::YearMonthDay(year, month, day) => {
641            Ok(year_month_day_to_daterange(*year, *month, *day)?)
642        }
643        DateTime::Iso8601(date_time) => {
644            return Ok(DateRange {
645                start: date_time.timestamp_millis(),
646                end: date_time.timestamp_millis(),
647            });
648        }
649    }
650}
651
652fn index_date(value: &dyn MetaValue) -> Result<Vec<DateRange>, InsertableIndexError> {
653    match value.typename() {
654        "Timing" => {
655            let fp_timing = value.as_any().downcast_ref::<Timing>().ok_or_else(|| {
656                InsertableIndexError::FailedDowncast(value.typename().to_string())
657            })?;
658
659            if let Some(events) = fp_timing.event.as_ref() {
660                let date_ranges = events
661                    .iter()
662                    .map(|event| index_date(event.as_ref()))
663                    .collect::<Result<Vec<_>, _>>()?;
664                Ok(date_ranges.into_iter().flatten().collect())
665            } else {
666                Ok(vec![])
667            }
668        }
669        "FHIRDate" => {
670            let fp_date = value
671                .as_any()
672                .downcast_ref::<FHIRDate>()
673                .ok_or_else(|| InsertableIndexError::FailedDowncast(value.typename().to_string()))?
674                .value
675                .as_ref();
676
677            match &fp_date {
678                Some(Date::Year(year)) => Ok(vec![year_to_daterange(*year)?]),
679                Some(Date::YearMonth(year, month)) => {
680                    Ok(vec![year_month_to_daterange(*year, *month)?])
681                }
682                Some(Date::YearMonthDay(year, month, day)) => {
683                    Ok(vec![year_month_day_to_daterange(*year, *month, *day)?])
684                }
685                None => Ok(vec![]),
686            }
687        }
688        "FHIRDateTime" => {
689            let fp_datetime = value
690                .as_any()
691                .downcast_ref::<FHIRDateTime>()
692                .ok_or_else(|| InsertableIndexError::FailedDowncast(value.typename().to_string()))?
693                .value
694                .as_ref();
695
696            match &fp_datetime {
697                Some(date_time) => date_time_range(date_time).map(|date_range| vec![date_range]),
698                None => {
699                    return Ok(vec![]);
700                }
701            }
702        }
703        "FHIRInstant" => {
704            let fp_instant = value
705                .as_any()
706                .downcast_ref::<FHIRInstant>()
707                .ok_or_else(|| {
708                    InsertableIndexError::FailedDowncast(value.typename().to_string())
709                })?;
710
711            match &fp_instant.value {
712                Some(Instant::Iso8601(instant)) => {
713                    let timestamp = instant.timestamp_millis();
714                    return Ok(vec![DateRange {
715                        start: timestamp,
716                        end: timestamp,
717                    }]);
718                }
719                None => {
720                    return Ok(vec![]);
721                }
722            }
723        }
724        "Period" => {
725            let fp_period = value.as_any().downcast_ref::<Period>().ok_or_else(|| {
726                InsertableIndexError::FailedDowncast(value.typename().to_string())
727            })?;
728            let fp_start = if let Some(date) = fp_period.start.as_ref() {
729                let date = date.as_ref();
730                let date_range = index_date(date)?;
731                date_range
732                    .get(0)
733                    .ok_or_else(|| {
734                        InsertableIndexError::FailedDowncast(value.typename().to_string())
735                    })?
736                    .start
737            } else {
738                0
739            };
740
741            let fp_end = if let Some(date) = fp_period.end.as_ref() {
742                let date = date.as_ref();
743                let date_range = index_date(date)?;
744                date_range
745                    .get(0)
746                    .ok_or_else(|| {
747                        InsertableIndexError::FailedDowncast(value.typename().to_string())
748                    })?
749                    .end
750            } else {
751                i64::MAX
752            };
753
754            Ok(vec![DateRange {
755                start: fp_start,
756                end: fp_end,
757            }])
758        }
759        _ => Err(InsertableIndexError::FailedDowncast(
760            value.typename().to_string(),
761        )),
762    }
763}
764
765fn index_reference(value: &dyn MetaValue) -> Result<Vec<ReferenceIndex>, InsertableIndexError> {
766    match value.typename() {
767        "Reference" => {
768            let fp_reference = value.as_any().downcast_ref::<Reference>().ok_or_else(|| {
769                InsertableIndexError::FailedDowncast(value.typename().to_string())
770            })?;
771
772            if let Some(reference) = &fp_reference
773                .reference
774                .as_ref()
775                .and_then(|r| r.value.as_ref())
776            {
777                let parts: Vec<&str> = reference.split('/').collect();
778                if parts.len() == 2 {
779                    let resource_type = ResourceType::try_from(parts[0])?;
780                    let id = parts[1].to_string();
781                    return Ok(vec![ReferenceIndex {
782                        resource_type: Some(resource_type.as_ref().to_string()),
783                        id: Some(id),
784                        uri: None,
785                    }]);
786                }
787            }
788
789            Ok(vec![])
790        }
791        "FHIRCanonical" => {
792            let fp_canonical = value
793                .as_any()
794                .downcast_ref::<FHIRCanonical>()
795                .ok_or_else(|| {
796                    InsertableIndexError::FailedDowncast(value.typename().to_string())
797                })?;
798            if let Some(canonical) = &fp_canonical.value {
799                return Ok(vec![ReferenceIndex {
800                    id: None,
801                    resource_type: None,
802                    uri: Some(canonical.to_string()),
803                }]);
804            }
805            Ok(vec![])
806        }
807        "FHIRUri" => {
808            let fp_uri = value.as_any().downcast_ref::<FHIRUri>().ok_or_else(|| {
809                InsertableIndexError::FailedDowncast(value.typename().to_string())
810            })?;
811            if let Some(uri) = &fp_uri.value {
812                return Ok(vec![ReferenceIndex {
813                    id: None,
814                    resource_type: None,
815                    uri: Some(uri.to_string()),
816                }]);
817            }
818            Ok(vec![])
819        }
820        _ => Err(InsertableIndexError::FailedDowncast(
821            value.typename().to_string(),
822        )),
823    }
824}
825
826pub fn to_insertable_index(
827    parameter: &SearchParameter,
828    result: Vec<&dyn MetaValue>,
829) -> Result<InsertableIndex, OperationOutcomeError> {
830    match parameter.type_.as_ref() {
831        SearchParamType::Number(_) => {
832            let numbers = result
833                .iter()
834                .filter_map(|v| index_number(*v).ok())
835                .flatten()
836                .collect::<Vec<_>>();
837            Ok(InsertableIndex::Number(numbers))
838        }
839        SearchParamType::String(_) => {
840            let strings = result
841                .iter()
842                .filter_map(|v| index_string(*v).ok())
843                .flatten()
844                .collect();
845            Ok(InsertableIndex::String(strings))
846        }
847        SearchParamType::Uri(_) => {
848            let uris = result
849                .iter()
850                .filter_map(|v| index_uri(*v).ok())
851                .flatten()
852                .collect();
853            Ok(InsertableIndex::URI(uris))
854        }
855        SearchParamType::Token(_) => {
856            let tokens = result
857                .iter()
858                .filter_map(|v| index_token(*v).ok())
859                .flatten()
860                .collect();
861            Ok(InsertableIndex::Token(tokens))
862        }
863        SearchParamType::Date(_) => {
864            let dates = result
865                .iter()
866                .filter_map(|v| index_date(*v).ok())
867                .flatten()
868                .collect();
869            Ok(InsertableIndex::Date(dates))
870        }
871        SearchParamType::Reference(_) => {
872            let references = result
873                .iter()
874                .filter_map(|v: &&dyn MetaValue| index_reference(*v).ok())
875                .flatten()
876                .collect();
877            Ok(InsertableIndex::Reference(references))
878        }
879        SearchParamType::Quantity(_) => {
880            let quantities = result
881                .iter()
882                .filter_map(|v| index_quantity(*v).ok())
883                .flatten()
884                .collect();
885            Ok(InsertableIndex::Quantity(quantities))
886        }
887        // Not Supported yet
888        SearchParamType::Composite(_) => Ok(InsertableIndex::Composite(vec![])),
889        SearchParamType::Special(_) => Ok(InsertableIndex::Special(vec![])),
890        _ => {
891            let type_name: Option<String> = parameter.type_.as_ref().into();
892            Err(
893                InsertableIndexError::InvalidType(type_name.unwrap_or("unknown".to_string()))
894                    .into(),
895            )
896        }
897    }
898}
899
900#[cfg(test)]
901mod tests {
902    use super::*;
903    use haste_fhir_model::r4::generated::types::{
904        FHIRDate, FHIRDateTime, Period, Reference, Timing,
905    };
906
907    #[test]
908    fn test_year_month_to_daterange() {
909        let year = 2023;
910        let month: u8 = 5;
911        let date_range = year_month_to_daterange(year, month).unwrap();
912
913        assert_eq!(
914            date_range.start,
915            chrono::DateTime::parse_from_rfc3339("2023-05-01T00:00:00Z")
916                .unwrap()
917                .timestamp_millis()
918        );
919        assert_eq!(
920            date_range.end,
921            chrono::DateTime::parse_from_rfc3339("2023-05-31T23:59:59.999Z")
922                .unwrap()
923                .timestamp_millis()
924        );
925    }
926
927    #[test]
928    fn test_year_month_day_to_daterange() {
929        let year = 2023;
930        let month: u8 = 5;
931        let day = 15;
932        let date_range = year_month_day_to_daterange(year, month, day).unwrap();
933
934        assert_eq!(
935            date_range.start,
936            chrono::DateTime::parse_from_rfc3339("2023-05-15T00:00:00Z")
937                .unwrap()
938                .timestamp_millis()
939        );
940        assert_eq!(
941            date_range.end,
942            chrono::DateTime::parse_from_rfc3339("2023-05-15T23:59:59.999Z")
943                .unwrap()
944                .timestamp_millis()
945        );
946    }
947
948    #[test]
949    fn test_year_to_daterange() {
950        let year = 2023;
951        let date_range = year_to_daterange(year).unwrap();
952        assert_eq!(
953            date_range.start,
954            chrono::DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z")
955                .unwrap()
956                .timestamp_millis()
957        );
958        assert_eq!(
959            date_range.end,
960            chrono::DateTime::parse_from_rfc3339("2023-12-31T23:59:59.999Z")
961                .unwrap()
962                .timestamp_millis()
963        );
964    }
965
966    #[test]
967    fn test_index_date() {
968        let date_value = FHIRDate {
969            id: None,
970            extension: None,
971            value: Some(Date::Year(2023)),
972        };
973        let result = index_date(&date_value).unwrap();
974        assert_eq!(result.len(), 1);
975        assert_eq!(
976            result[0].start,
977            chrono::DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z")
978                .unwrap()
979                .timestamp_millis()
980        );
981        assert_eq!(
982            result[0].end,
983            chrono::DateTime::parse_from_rfc3339("2023-12-31T23:59:59.999Z")
984                .unwrap()
985                .timestamp_millis()
986        );
987    }
988
989    #[test]
990    fn date_range_instant() {
991        let fhir_date = FHIRDateTime {
992            id: None,
993            extension: None,
994            value: Some(DateTime::Iso8601(
995                chrono::DateTime::parse_from_rfc3339("2023-05-14T11:25:25.234-05:00")
996                    .unwrap()
997                    .with_timezone(&chrono::Utc),
998            )),
999        };
1000
1001        let range = index_date(&fhir_date).unwrap();
1002        let date_range = range.get(0).unwrap();
1003
1004        assert_eq!(
1005            date_range.start,
1006            chrono::DateTime::parse_from_rfc3339("2023-05-14T11:25:25.234-05:00")
1007                .unwrap()
1008                .with_timezone(&chrono::Utc)
1009                .timestamp_millis()
1010        );
1011        assert_eq!(
1012            date_range.end,
1013            chrono::DateTime::parse_from_rfc3339("2023-05-14T11:25:25.234-05:00")
1014                .unwrap()
1015                .with_timezone(&chrono::Utc)
1016                .timestamp_millis()
1017        );
1018    }
1019
1020    #[test]
1021    fn date_range_period() {
1022        let start = FHIRDateTime {
1023            id: None,
1024            extension: None,
1025            value: Some(DateTime::Year(2023)),
1026        };
1027
1028        let end = FHIRDateTime {
1029            id: None,
1030            extension: None,
1031            value: Some(DateTime::YearMonthDay(2023, 5, 15)),
1032        };
1033
1034        let period = Period {
1035            id: None,
1036            extension: None,
1037            start: Some(Box::new(start)),
1038            end: Some(Box::new(end)),
1039        };
1040
1041        let range = index_date(&period).unwrap();
1042        let date_range = range.get(0).unwrap();
1043
1044        assert_eq!(
1045            date_range.start,
1046            chrono::DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z")
1047                .unwrap()
1048                .timestamp_millis()
1049        );
1050        assert_eq!(
1051            date_range.end,
1052            chrono::DateTime::parse_from_rfc3339("2023-05-15T23:59:59.999Z")
1053                .unwrap()
1054                .timestamp_millis()
1055        );
1056    }
1057
1058    #[test]
1059    fn date_range_missing() {
1060        let start = FHIRDateTime {
1061            id: None,
1062            extension: None,
1063            value: Some(DateTime::Year(2023)),
1064        };
1065
1066        let end = FHIRDateTime {
1067            id: None,
1068            extension: None,
1069            value: Some(DateTime::YearMonthDay(2023, 5, 15)),
1070        };
1071
1072        let period = Period {
1073            id: None,
1074            extension: None,
1075            start: None,
1076            end: Some(Box::new(end)),
1077        };
1078
1079        let range = index_date(&period).unwrap();
1080        let date_range = range.get(0).unwrap();
1081
1082        assert_eq!(date_range.start, 0);
1083        assert_eq!(
1084            date_range.end,
1085            chrono::DateTime::parse_from_rfc3339("2023-05-15T23:59:59.999Z")
1086                .unwrap()
1087                .timestamp_millis()
1088        );
1089
1090        let period = Period {
1091            id: None,
1092            extension: None,
1093            start: Some(Box::new(start)),
1094            end: None,
1095        };
1096
1097        let range = index_date(&period).unwrap();
1098        let date_range = range.get(0).unwrap();
1099
1100        assert_eq!(
1101            date_range.start,
1102            chrono::DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z")
1103                .unwrap()
1104                .timestamp_millis()
1105        );
1106        assert_eq!(date_range.end, i64::MAX);
1107    }
1108
1109    #[test]
1110    fn test_date_range_end() {
1111        let year = 2023;
1112        let month: u8 = 12;
1113        let day = 31;
1114        let date_range = year_month_day_to_daterange(year, month, day).unwrap();
1115
1116        assert_eq!(
1117            date_range.start,
1118            chrono::DateTime::parse_from_rfc3339("2023-12-31T00:00:00Z")
1119                .unwrap()
1120                .timestamp_millis()
1121        );
1122
1123        assert_eq!(
1124            date_range.end,
1125            chrono::DateTime::parse_from_rfc3339("2023-12-31T23:59:59.999Z")
1126                .unwrap()
1127                .timestamp_millis()
1128        );
1129
1130        let date_range = year_month_to_daterange(year, month).unwrap();
1131
1132        assert_eq!(
1133            date_range.start,
1134            chrono::DateTime::parse_from_rfc3339("2023-12-01T00:00:00Z")
1135                .unwrap()
1136                .timestamp_millis()
1137        );
1138
1139        assert_eq!(
1140            date_range.end,
1141            chrono::DateTime::parse_from_rfc3339("2023-12-31T23:59:59.999Z")
1142                .unwrap()
1143                .timestamp_millis()
1144        );
1145    }
1146
1147    #[test]
1148    fn test_timing() {
1149        let mut timing = Timing::default();
1150        timing.event = Some(vec![
1151            Box::new(FHIRDateTime {
1152                id: None,
1153                extension: None,
1154                value: Some(DateTime::YearMonthDay(2023, 12, 31)),
1155            }),
1156            Box::new(FHIRDateTime {
1157                id: None,
1158                extension: None,
1159                value: Some(DateTime::YearMonthDay(2024, 1, 1)),
1160            }),
1161        ]);
1162
1163        let date_ranges = index_date(&timing).unwrap();
1164        assert_eq!(date_ranges.len(), 2);
1165
1166        assert_eq!(
1167            date_ranges[0],
1168            DateRange {
1169                start: chrono::DateTime::parse_from_rfc3339("2023-12-31T00:00:00Z")
1170                    .unwrap()
1171                    .timestamp_millis(),
1172                end: chrono::DateTime::parse_from_rfc3339("2023-12-31T23:59:59.999Z")
1173                    .unwrap()
1174                    .timestamp_millis(),
1175            }
1176        );
1177
1178        assert_eq!(
1179            date_ranges[1],
1180            DateRange {
1181                start: chrono::DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z")
1182                    .unwrap()
1183                    .timestamp_millis(),
1184                end: chrono::DateTime::parse_from_rfc3339("2024-01-01T23:59:59.999Z")
1185                    .unwrap()
1186                    .timestamp_millis(),
1187            }
1188        );
1189    }
1190
1191    #[test]
1192    fn test_indexing_reference() {
1193        let reference = Reference {
1194            type_: None,
1195            identifier_: None,
1196            display: None,
1197            id: None,
1198            extension: None,
1199            reference: Some(Box::new(FHIRString {
1200                id: None,
1201                extension: None,
1202                value: Some("Patient/123".to_string()),
1203            })),
1204        };
1205
1206        let result = index_reference(&reference).unwrap();
1207        assert_eq!(result.len(), 1);
1208        assert_eq!(result[0].resource_type, Some("Patient".to_string()));
1209        assert_eq!(result[0].id, Some("123".to_string()));
1210        assert!(result[0].uri.is_none());
1211    }
1212
1213    #[test]
1214    fn test_indexing_invalid_reference() {
1215        let reference = Reference {
1216            type_: None,
1217            identifier_: None,
1218            display: None,
1219            id: None,
1220            extension: None,
1221            reference: Some(Box::new(FHIRString {
1222                id: None,
1223                extension: None,
1224                value: Some("BadType/123".to_string()),
1225            })),
1226        };
1227
1228        let result = index_reference(&reference);
1229
1230        assert!(result.is_err())
1231    }
1232
1233    #[test]
1234    fn test_canonical_index() {
1235        let canonical = FHIRCanonical {
1236            id: None,
1237            extension: None,
1238            value: Some("http://example.com/CanonicalResource".to_string()),
1239        };
1240        let result = index_reference(&canonical).unwrap();
1241        assert_eq!(result.len(), 1);
1242        assert!(result[0].id.is_none());
1243        assert!(result[0].resource_type.is_none());
1244        assert_eq!(
1245            result[0].uri,
1246            Some("http://example.com/CanonicalResource".to_string())
1247        );
1248    }
1249
1250    #[test]
1251    fn test_uri_indexing() {
1252        let uri = FHIRUri {
1253            id: None,
1254            extension: None,
1255            value: Some("http://example.com/URIResource".to_string()),
1256        };
1257        let result = index_reference(&uri).unwrap();
1258        assert_eq!(result.len(), 1);
1259        assert!(result[0].id.is_none());
1260        assert!(result[0].resource_type.is_none());
1261        assert_eq!(
1262            result[0].uri,
1263            Some("http://example.com/URIResource".to_string())
1264        );
1265    }
1266}