1use std::collections::HashMap;
2
3use 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 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 Meta(String),
65 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
97fn 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 "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
432pub 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 = ¶meter.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 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}