Skip to main content

haste_fhir_search/
indexing_conversion.rs

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