1use 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 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 Meta(String),
61 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
92fn 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 "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
427pub 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 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}