1use haste_fhir_model::r4::{
3 datetime::{Date, DateTime, Instant},
4 generated::{
5 resources::{ResourceType, ResourceTypeError},
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 haste_repository::utilities::parse_haste_canonical;
18use serde::{Deserialize, Serialize};
19
20use crate::ResolvedParameter;
21
22#[derive(Debug, Serialize, Deserialize)]
23pub struct TokenIndex {
24 system: Option<String>,
25 code: Option<String>,
26}
27
28impl TokenIndex {
29 #[must_use]
30 pub fn system(&self) -> Option<&str> {
31 self.system.as_deref()
32 }
33
34 #[must_use]
35 pub fn code(&self) -> Option<&str> {
36 self.code.as_deref()
37 }
38}
39
40#[derive(Debug, Serialize, Deserialize)]
41#[serde(untagged)]
42enum RangeValue {
43 Number(f64),
44 Infinity,
45}
46
47impl RangeValue {
48 fn as_f64(&self) -> f64 {
49 match self {
50 RangeValue::Number(n) => *n,
51 RangeValue::Infinity => f64::INFINITY,
52 }
53 }
54}
55
56#[derive(Debug, Serialize, Deserialize)]
57pub struct QuantityRange {
58 start_value: RangeValue,
59 start_code: Option<String>,
60 start_system: Option<String>,
61 end_value: RangeValue,
62 end_code: Option<String>,
63 end_system: Option<String>,
64}
65
66impl QuantityRange {
67 #[must_use]
68 pub fn start_value(&self) -> f64 {
69 self.start_value.as_f64()
70 }
71
72 #[must_use]
73 pub fn start_code(&self) -> Option<&str> {
74 self.start_code.as_deref()
75 }
76
77 #[must_use]
78 pub fn start_system(&self) -> Option<&str> {
79 self.start_system.as_deref()
80 }
81
82 #[must_use]
83 pub fn end_value(&self) -> f64 {
84 self.end_value.as_f64()
85 }
86
87 #[must_use]
88 pub fn end_code(&self) -> Option<&str> {
89 self.end_code.as_deref()
90 }
91
92 #[must_use]
93 pub fn end_system(&self) -> Option<&str> {
94 self.end_system.as_deref()
95 }
96}
97
98#[derive(Debug, Serialize, Deserialize, PartialEq)]
99pub struct DateRange {
100 pub start: i64,
102 pub end: i64,
103}
104
105#[derive(Debug, Serialize, Deserialize)]
106pub struct ReferenceIndex {
107 id: Option<String>,
108 resource_type: Option<String>,
109 uri: Option<String>,
110}
111
112impl ReferenceIndex {
113 #[must_use]
114 pub fn id(&self) -> Option<&str> {
115 self.id.as_deref()
116 }
117
118 #[must_use]
119 pub fn resource_type(&self) -> Option<&str> {
120 self.resource_type.as_deref()
121 }
122
123 #[must_use]
124 pub fn uri(&self) -> Option<&str> {
125 self.uri.as_deref()
126 }
127}
128
129#[derive(Debug, Serialize, Deserialize, Default)]
138pub struct DynamicParameterValue {
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub string: Option<Vec<String>>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub number: Option<Vec<f64>>,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub uri: Option<Vec<String>>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub token: Option<Vec<TokenIndex>>,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub date: Option<Vec<DateRange>>,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub reference: Option<Vec<ReferenceIndex>>,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub quantity: Option<Vec<QuantityRange>>,
153}
154
155#[derive(Debug, Serialize, Deserialize)]
156pub struct DynamicParameterEntry {
157 pub url: String,
158 #[serde(rename = "type")]
159 pub type_: String,
160 pub value: DynamicParameterValue,
161}
162
163impl DynamicParameterEntry {
164 #[must_use]
170 pub fn from_leaf(url: String, type_: &str, leaf: InsertableIndex) -> Option<Self> {
171 let value = match leaf {
172 InsertableIndex::String(v) => DynamicParameterValue {
173 string: Some(v),
174 ..Default::default()
175 },
176 InsertableIndex::Number(v) => DynamicParameterValue {
177 number: Some(v),
178 ..Default::default()
179 },
180 InsertableIndex::URI(v) => DynamicParameterValue {
181 uri: Some(v),
182 ..Default::default()
183 },
184 InsertableIndex::Token(v) => DynamicParameterValue {
185 token: Some(v),
186 ..Default::default()
187 },
188 InsertableIndex::Date(v) => DynamicParameterValue {
189 date: Some(v),
190 ..Default::default()
191 },
192 InsertableIndex::Reference(v) => DynamicParameterValue {
193 reference: Some(v),
194 ..Default::default()
195 },
196 InsertableIndex::Quantity(v) => DynamicParameterValue {
197 quantity: Some(v),
198 ..Default::default()
199 },
200 InsertableIndex::Meta(_)
201 | InsertableIndex::Composite(_)
202 | InsertableIndex::Special(_)
203 | InsertableIndex::DynamicParameters(_) => return None,
204 };
205
206 Some(DynamicParameterEntry {
207 url,
208 type_: type_.to_string(),
209 value,
210 })
211 }
212}
213
214#[derive(Debug, Serialize, Deserialize)]
215#[serde(untagged)]
216pub enum InsertableIndex {
217 Meta(String),
219 String(Vec<String>),
221 Number(Vec<f64>),
222 URI(Vec<String>),
223 Token(Vec<TokenIndex>),
224 Date(Vec<DateRange>),
225 Reference(Vec<ReferenceIndex>),
226 Quantity(Vec<QuantityRange>),
227 Composite(Vec<String>),
228 Special(Vec<String>),
229 DynamicParameters(Vec<DynamicParameterEntry>),
230}
231
232#[derive(OperationOutcomeError, Debug)]
233pub enum InsertableIndexError {
234 #[fatal(
235 code = "exception",
236 diagnostic = "Invalid type for insertable index: '{arg0}'"
237 )]
238 InvalidType(String),
239 #[fatal(
240 code = "exception",
241 diagnostic = "Failed to downcast value to number: {arg0}"
242 )]
243 FailedDowncast(String),
244 #[fatal(
245 code = "exception",
246 diagnostic = "Reference contains invalid resource type."
247 )]
248 ResourceTypeError(#[from] ResourceTypeError),
249}
250
251fn convert_fp_string(value: &FHIRString) -> Vec<String> {
258 value
259 .value
260 .as_ref()
261 .map_or_else(Vec::new, |v| vec![v.clone()])
262}
263
264fn convert_optional_fp_string(value: Option<&FHIRString>) -> Vec<String> {
265 value
266 .as_ref()
267 .map_or_else(Vec::new, |v| convert_fp_string(v))
268}
269
270fn convert_optional_fp_string_vec(value: Option<&Vec<FHIRString>>) -> Vec<String> {
271 value
272 .as_ref()
273 .map_or_else(Vec::new, |v| v.iter().flat_map(convert_fp_string).collect())
274}
275
276fn index_string(value: &dyn MetaValue) -> Result<Vec<String>, InsertableIndexError> {
277 match value.fhir_type() {
278 "string" => {
279 let fp_string = value.as_any().downcast_ref::<FHIRString>().ok_or_else(|| {
280 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
281 })?;
282 Ok(fp_string
283 .value
284 .as_ref()
285 .map_or_else(Vec::new, |v| vec![v.clone()]))
286 }
287 "markdown" => {
289 let fp_markdown = value
290 .as_any()
291 .downcast_ref::<FHIRMarkdown>()
292 .ok_or_else(|| {
293 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
294 })?;
295 Ok(fp_markdown
296 .value
297 .as_ref()
298 .map_or_else(Vec::new, |v| vec![v.clone()]))
299 }
300 "HumanName" => {
301 let human_name = value.as_any().downcast_ref::<HumanName>().ok_or_else(|| {
302 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
303 })?;
304
305 let mut string_index = Vec::new();
306 string_index.extend(convert_optional_fp_string(human_name.text.as_deref()));
307 string_index.extend(convert_optional_fp_string(human_name.family.as_deref()));
308 string_index.extend(convert_optional_fp_string_vec(human_name.given.as_ref()));
309 string_index.extend(convert_optional_fp_string_vec(human_name.prefix.as_ref()));
310 string_index.extend(convert_optional_fp_string_vec(human_name.suffix.as_ref()));
311 Ok(string_index)
312 }
313 "Address" => {
314 let mut string_index = Vec::new();
315 let address = value.as_any().downcast_ref::<Address>().ok_or_else(|| {
316 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
317 })?;
318 string_index.extend(convert_optional_fp_string(address.text.as_deref()));
319 string_index.extend(convert_optional_fp_string_vec(address.line.as_ref()));
320 string_index.extend(convert_optional_fp_string(address.city.as_deref()));
321 string_index.extend(convert_optional_fp_string(address.district.as_deref()));
322 string_index.extend(convert_optional_fp_string(address.state.as_deref()));
323 string_index.extend(convert_optional_fp_string(address.postalCode.as_deref()));
324 string_index.extend(convert_optional_fp_string(address.country.as_deref()));
325
326 Ok(string_index)
327 }
328
329 type_name => Err(InsertableIndexError::FailedDowncast(type_name.to_string())),
330 }
331}
332
333fn index_number(value: &dyn MetaValue) -> Result<Vec<f64>, InsertableIndexError> {
334 match value.fhir_type() {
335 "integer" => {
336 let fp_integer = value
337 .as_any()
338 .downcast_ref::<FHIRInteger>()
339 .ok_or_else(|| {
340 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
341 })?;
342 #[allow(clippy::cast_precision_loss)]
343 Ok(fp_integer
344 .value
345 .as_ref()
346 .map_or_else(Vec::new, |v| vec![*v as f64]))
347 }
348 "decimal" => {
349 let fp_decimal = value
350 .as_any()
351 .downcast_ref::<FHIRDecimal>()
352 .ok_or_else(|| {
353 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
354 })?;
355 #[allow(clippy::cast_precision_loss)]
356 Ok(fp_decimal
357 .value
358 .as_ref()
359 .map_or_else(Vec::new, |v| vec![*v]))
360 }
361 "positiveInt" => {
362 let fp_positive_int = value
363 .as_any()
364 .downcast_ref::<FHIRPositiveInt>()
365 .ok_or_else(|| {
366 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
367 })?;
368
369 #[allow(clippy::cast_precision_loss)]
370 Ok(fp_positive_int
371 .value
372 .as_ref()
373 .map_or_else(Vec::new, |v| vec![*v as f64]))
374 }
375 "unsignedInt" => {
376 let fp_unsigned_int = value
377 .as_any()
378 .downcast_ref::<FHIRUnsignedInt>()
379 .ok_or_else(|| {
380 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
381 })?;
382
383 #[allow(clippy::cast_precision_loss)]
384 Ok(fp_unsigned_int
385 .value
386 .as_ref()
387 .map_or_else(Vec::new, |v| vec![*v as f64]))
388 }
389 type_name => Err(InsertableIndexError::FailedDowncast(type_name.to_string())),
390 }
391}
392
393fn index_uri(value: &dyn MetaValue) -> Result<Vec<String>, InsertableIndexError> {
394 match value.fhir_type() {
395 "url" => {
396 let fp_uri = value.as_any().downcast_ref::<FHIRUrl>().ok_or_else(|| {
397 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
398 })?;
399 Ok(fp_uri
400 .value
401 .as_ref()
402 .map_or_else(Vec::new, |v| vec![v.clone()]))
403 }
404 "uuid" => {
405 let fp_uri = value.as_any().downcast_ref::<FHIRUuid>().ok_or_else(|| {
406 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
407 })?;
408 Ok(fp_uri
409 .value
410 .as_ref()
411 .map_or_else(Vec::new, |v| vec![v.clone()]))
412 }
413 "canonical" => {
414 let fp_uri = value
415 .as_any()
416 .downcast_ref::<FHIRCanonical>()
417 .ok_or_else(|| {
418 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
419 })?;
420 Ok(fp_uri
421 .value
422 .as_ref()
423 .map_or_else(Vec::new, |v| vec![v.clone()]))
424 }
425 "uri" => {
426 let fp_uri = value.as_any().downcast_ref::<FHIRUri>().ok_or_else(|| {
427 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
428 })?;
429 Ok(fp_uri
430 .value
431 .as_ref()
432 .map_or_else(Vec::new, |v| vec![v.clone()]))
433 }
434 type_name => Err(InsertableIndexError::FailedDowncast(type_name.to_string())),
435 }
436}
437
438fn index_token(value: &dyn MetaValue) -> Result<Vec<TokenIndex>, InsertableIndexError> {
439 match value.fhir_type() {
440 "Coding" => index_token_coding(value),
441 "CodeableConcept" => index_token_codeable_concept(value),
442 "Identifier" => index_token_identifier(value),
443 "ContactPoint" => index_token_contact_point(value),
444 "code" => Ok(index_token_code(value)),
445 "boolean" => index_token_boolean(value),
446 "http://hl7.org/fhirpath/System.String" => index_token_system_string(value),
447 "string" => index_token_string(value),
448 "id" => index_token_id(value),
449 _ => Err(InsertableIndexError::FailedDowncast(
450 value.fhir_type().to_string(),
451 )),
452 }
453}
454
455fn index_token_coding(value: &dyn MetaValue) -> Result<Vec<TokenIndex>, InsertableIndexError> {
456 let coding = value
457 .as_any()
458 .downcast_ref::<Coding>()
459 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
460
461 Ok(vec![TokenIndex {
462 system: coding.system.as_ref().and_then(|s| s.value.clone()),
463 code: coding.code.as_ref().and_then(|v| v.value.clone()),
464 }])
465}
466
467fn index_token_codeable_concept(
468 value: &dyn MetaValue,
469) -> Result<Vec<TokenIndex>, InsertableIndexError> {
470 let codeable_concept = value
471 .as_any()
472 .downcast_ref::<CodeableConcept>()
473 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
474
475 Ok(codeable_concept
476 .coding
477 .as_ref()
478 .map(|coding| {
479 coding
480 .iter()
481 .map(|c| TokenIndex {
482 system: c.system.as_ref().and_then(|s| s.value.clone()),
483 code: c.code.as_ref().and_then(|v| v.value.clone()),
484 })
485 .collect()
486 })
487 .unwrap_or_default())
488}
489
490fn index_token_identifier(value: &dyn MetaValue) -> Result<Vec<TokenIndex>, InsertableIndexError> {
491 let identifier = value
492 .as_any()
493 .downcast_ref::<Identifier>()
494 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
495
496 Ok(vec![TokenIndex {
497 system: identifier.system.as_ref().and_then(|s| s.value.clone()),
498 code: identifier.value.as_ref().and_then(|v| v.value.clone()),
499 }])
500}
501
502fn index_token_contact_point(
503 value: &dyn MetaValue,
504) -> Result<Vec<TokenIndex>, InsertableIndexError> {
505 let contact_point = value
506 .as_any()
507 .downcast_ref::<ContactPoint>()
508 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
509
510 Ok(vec![TokenIndex {
511 system: None,
512 code: contact_point.value.as_ref().and_then(|v| v.value.clone()),
513 }])
514}
515
516fn index_token_code(value: &dyn MetaValue) -> Vec<TokenIndex> {
517 let code = value
518 .get_field("value")
519 .and_then(|v| v.as_any().downcast_ref::<String>());
520
521 vec![TokenIndex {
522 system: None,
523 code: code.cloned(),
524 }]
525}
526
527fn index_token_boolean(value: &dyn MetaValue) -> Result<Vec<TokenIndex>, InsertableIndexError> {
528 let boolean = value
529 .as_any()
530 .downcast_ref::<FHIRBoolean>()
531 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
532
533 Ok(vec![TokenIndex {
534 system: Some("http://hl7.org/fhir/special-values".to_string()),
535 code: boolean.value.as_ref().map(std::string::ToString::to_string),
536 }])
537}
538
539fn index_token_system_string(
540 value: &dyn MetaValue,
541) -> Result<Vec<TokenIndex>, InsertableIndexError> {
542 let string = value
543 .as_any()
544 .downcast_ref::<String>()
545 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
546
547 Ok(vec![TokenIndex {
548 system: None,
549 code: Some(string.clone()),
550 }])
551}
552
553fn index_token_string(value: &dyn MetaValue) -> Result<Vec<TokenIndex>, InsertableIndexError> {
554 let string = value
555 .as_any()
556 .downcast_ref::<FHIRString>()
557 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
558
559 Ok(vec![TokenIndex {
560 system: None,
561 code: string.value.clone(),
562 }])
563}
564
565fn index_token_id(value: &dyn MetaValue) -> Result<Vec<TokenIndex>, InsertableIndexError> {
566 let id = value
567 .as_any()
568 .downcast_ref::<FHIRId>()
569 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
570
571 Ok(vec![TokenIndex {
572 system: None,
573 code: id.value.clone(),
574 }])
575}
576
577fn get_decimal_precision(value: &str) -> i32 {
583 let (mantissa, exponent) = match value.split_once(['e', 'E']) {
584 Some((mantissa, exponent)) => (mantissa, exponent.parse::<i32>().unwrap_or(0)),
585 None => (value, 0),
586 };
587 let fraction_digits = mantissa
588 .split_once('.')
589 .map_or(0, |(_, fraction)| fraction.len());
590 i32::try_from(fraction_digits)
591 .unwrap_or(i32::MAX)
592 .saturating_sub(exponent)
593}
594
595#[derive(Debug)]
596pub struct DecimalRange {
597 pub start: f64,
598 pub end: f64,
599}
600
601pub fn get_decimal_range(value: &str) -> Result<DecimalRange, InsertableIndexError> {
609 let decimal_precision = get_decimal_precision(value);
610 let parsed_v = value
611 .parse::<f64>()
612 .map_err(|_e| InsertableIndexError::FailedDowncast(value.to_string()))?;
613
614 let half_step = 0.5 * 10f64.powi(-decimal_precision);
615 Ok(DecimalRange {
616 start: parsed_v - half_step,
617 end: parsed_v + half_step,
618 })
619}
620
621fn fhirdecimal_to_quantity_range(value: Option<&FHIRDecimal>) -> Option<DecimalRange> {
622 value.as_ref().and_then(|v| {
623 v.value
624 .as_ref()
625 .and_then(|v| get_decimal_range(&v.to_string()).ok())
626 })
627}
628
629fn index_quantity(value: &dyn MetaValue) -> Result<Vec<QuantityRange>, InsertableIndexError> {
630 match value.fhir_type() {
631 "Range" => index_range_quantity(value),
632 "Age" => index_age_quantity(value),
633 "Money" => index_money_quantity(value),
634 "Duration" => index_duration_quantity(value),
635 "Quantity" => index_fhir_quantity(value),
636 _ => Err(InsertableIndexError::FailedDowncast(
637 value.fhir_type().to_string(),
638 )),
639 }
640}
641
642fn index_range_quantity(value: &dyn MetaValue) -> Result<Vec<QuantityRange>, InsertableIndexError> {
643 let fp_range = value
644 .as_any()
645 .downcast_ref::<Range>()
646 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
647
648 if fp_range.low.is_some() || fp_range.high.is_some() {
649 let start_value = fp_range
650 .low
651 .as_ref()
652 .and_then(|v| v.value.as_ref().and_then(|v| v.value));
653
654 let start_system = fp_range
655 .low
656 .as_ref()
657 .and_then(|v| v.system.as_ref().and_then(|s| s.value.clone()));
658
659 let start_code = fp_range
660 .low
661 .as_ref()
662 .and_then(|v| v.code.as_ref().and_then(|c| c.value.clone()));
663
664 let end_value = fp_range
665 .high
666 .as_ref()
667 .and_then(|v| v.value.as_ref().and_then(|v| v.value));
668
669 let end_system = fp_range
670 .high
671 .as_ref()
672 .and_then(|v| v.system.as_ref().and_then(|s| s.value.clone()));
673
674 let end_code = fp_range
675 .high
676 .as_ref()
677 .and_then(|v| v.code.as_ref().and_then(|c| c.value.clone()));
678
679 return Ok(vec![QuantityRange {
680 start_system,
681 start_code,
682 start_value: start_value.map_or(RangeValue::Infinity, RangeValue::Number),
683 end_system,
684 end_code,
685 end_value: end_value.map_or(RangeValue::Infinity, RangeValue::Number),
686 }]);
687 }
688
689 Ok(Vec::new())
690}
691
692fn index_age_quantity(value: &dyn MetaValue) -> Result<Vec<QuantityRange>, InsertableIndexError> {
693 let fp_age = value
694 .as_any()
695 .downcast_ref::<Age>()
696 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
697
698 Ok(fhirdecimal_to_quantity_range(fp_age.value.as_deref())
699 .map(|decimal_range| {
700 vec![QuantityRange {
701 start_system: fp_age.system.as_ref().and_then(|s| s.value.clone()),
702 start_code: fp_age.code.as_ref().and_then(|c| c.value.clone()),
703 start_value: RangeValue::Number(decimal_range.start),
704 end_system: fp_age.system.as_ref().and_then(|s| s.value.clone()),
705 end_code: fp_age.code.as_ref().and_then(|c| c.value.clone()),
706 end_value: RangeValue::Number(decimal_range.end),
707 }]
708 })
709 .unwrap_or_default())
710}
711
712fn index_money_quantity(value: &dyn MetaValue) -> Result<Vec<QuantityRange>, InsertableIndexError> {
713 let fp_money = value
714 .as_any()
715 .downcast_ref::<Money>()
716 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
717
718 Ok(fhirdecimal_to_quantity_range(fp_money.value.as_deref())
719 .map(|decimal_range| {
720 vec![QuantityRange {
721 start_system: Some("urn:iso:std:iso:4217".to_string()),
722 start_code: fp_money.currency.as_ref().and_then(|c| c.value.clone()),
723 start_value: RangeValue::Number(decimal_range.start),
724 end_system: Some("urn:iso:std:iso:4217".to_string()),
725 end_code: fp_money.currency.as_ref().and_then(|c| c.value.clone()),
726 end_value: RangeValue::Number(decimal_range.end),
727 }]
728 })
729 .unwrap_or_default())
730}
731
732fn index_duration_quantity(
733 value: &dyn MetaValue,
734) -> Result<Vec<QuantityRange>, InsertableIndexError> {
735 let fp_duration = value
736 .as_any()
737 .downcast_ref::<Duration>()
738 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
739
740 Ok(fhirdecimal_to_quantity_range(fp_duration.value.as_deref())
741 .map(|decimal_range| {
742 vec![QuantityRange {
743 start_system: fp_duration.system.as_ref().and_then(|s| s.value.clone()),
744 start_code: fp_duration.code.as_ref().and_then(|c| c.value.clone()),
745 start_value: RangeValue::Number(decimal_range.start),
746 end_system: fp_duration.system.as_ref().and_then(|s| s.value.clone()),
747 end_code: fp_duration.code.as_ref().and_then(|c| c.value.clone()),
748 end_value: RangeValue::Number(decimal_range.end),
749 }]
750 })
751 .unwrap_or_default())
752}
753
754fn index_fhir_quantity(value: &dyn MetaValue) -> Result<Vec<QuantityRange>, InsertableIndexError> {
755 let fp_quantity = value
756 .as_any()
757 .downcast_ref::<Quantity>()
758 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?;
759
760 Ok(fhirdecimal_to_quantity_range(fp_quantity.value.as_deref())
761 .map(|decimal_range| {
762 vec![QuantityRange {
763 start_system: fp_quantity.system.as_ref().and_then(|s| s.value.clone()),
764 start_code: fp_quantity.code.as_ref().and_then(|c| c.value.clone()),
765 start_value: RangeValue::Number(decimal_range.start),
766 end_system: fp_quantity.system.as_ref().and_then(|s| s.value.clone()),
767 end_code: fp_quantity.code.as_ref().and_then(|c| c.value.clone()),
768 end_value: RangeValue::Number(decimal_range.end),
769 }]
770 })
771 .unwrap_or_default())
772}
773
774fn year_to_daterange(year: u16) -> Result<DateRange, InsertableIndexError> {
775 let start_date = chrono::NaiveDate::from_ymd_opt(i32::from(year), 1, 1)
776 .and_then(|d| d.and_hms_opt(0, 0, 0))
777 .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
778
779 let end_date = chrono::NaiveDate::from_ymd_opt(i32::from(year) + 1, 1, 1)
780 .and_then(|d| d.pred_opt())
781 .and_then(|d| d.and_hms_milli_opt(23, 59, 59, 999))
782 .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
783
784 Ok(DateRange {
785 start: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(start_date, chrono::Utc)
786 .timestamp_millis(),
787 end: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(end_date, chrono::Utc)
788 .timestamp_millis(),
789 })
790}
791
792fn year_month_to_daterange(year: u16, month: u8) -> Result<DateRange, InsertableIndexError> {
793 let start_date = chrono::NaiveDate::from_ymd_opt(i32::from(year), u32::from(month), 1)
794 .and_then(|d| d.and_hms_opt(0, 0, 0))
795 .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
796
797 let end_date = if month < 12 {
798 chrono::NaiveDate::from_ymd_opt(i32::from(year), (month + 1).into(), 1)
799 .and_then(|d| d.pred_opt())
800 .and_then(|d| d.and_hms_milli_opt(23, 59, 59, 999))
801 } else {
802 chrono::NaiveDate::from_ymd_opt(i32::from(year) + 1, 1, 1)
803 .and_then(|d| d.pred_opt())
804 .and_then(|d| d.and_hms_milli_opt(23, 59, 59, 999))
805 }
806 .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
807
808 Ok(DateRange {
809 start: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(start_date, chrono::Utc)
810 .timestamp_millis(),
811 end: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(end_date, chrono::Utc)
812 .timestamp_millis(),
813 })
814}
815
816fn year_month_day_to_daterange(
817 year: u16,
818 month: u8,
819 day: u8,
820) -> Result<DateRange, InsertableIndexError> {
821 let start_date =
822 chrono::NaiveDate::from_ymd_opt(i32::from(year), u32::from(month), u32::from(day))
823 .and_then(|d| d.and_hms_opt(0, 0, 0))
824 .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
825
826 let end_date =
827 chrono::NaiveDate::from_ymd_opt(i32::from(year), u32::from(month), u32::from(day))
828 .and_then(|d| d.and_hms_milli_opt(23, 59, 59, 999))
829 .ok_or_else(|| InsertableIndexError::FailedDowncast("Date".to_string()))?;
830
831 Ok(DateRange {
832 start: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(start_date, chrono::Utc)
833 .timestamp_millis(),
834 end: chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(end_date, chrono::Utc)
835 .timestamp_millis(),
836 })
837}
838
839pub fn date_time_range(value: &DateTime) -> Result<DateRange, InsertableIndexError> {
847 match value {
848 DateTime::Year(year) => Ok(year_to_daterange(*year)?),
849 DateTime::YearMonth(year, month) => Ok(year_month_to_daterange(*year, *month)?),
850 DateTime::YearMonthDay(year, month, day) => {
851 Ok(year_month_day_to_daterange(*year, *month, *day)?)
852 }
853 DateTime::Iso8601(date_time) => Ok(DateRange {
854 start: date_time.timestamp_millis(),
855 end: date_time.timestamp_millis(),
856 }),
857 }
858}
859
860fn index_date(value: &dyn MetaValue) -> Result<Vec<DateRange>, InsertableIndexError> {
861 match value.fhir_type() {
862 "Timing" => {
863 let fp_timing = value.as_any().downcast_ref::<Timing>().ok_or_else(|| {
864 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
865 })?;
866
867 if let Some(events) = fp_timing.event.as_ref() {
868 let date_ranges = events
869 .iter()
870 .map(|event| index_date(event))
871 .collect::<Result<Vec<_>, _>>()?;
872 Ok(date_ranges.into_iter().flatten().collect())
873 } else {
874 Ok(Vec::new())
875 }
876 }
877 "date" => {
878 let fp_date = value
879 .as_any()
880 .downcast_ref::<FHIRDate>()
881 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?
882 .value
883 .as_ref();
884
885 match &fp_date {
886 Some(Date::Year(year)) => Ok(vec![year_to_daterange(*year)?]),
887 Some(Date::YearMonth(year, month)) => {
888 Ok(vec![year_month_to_daterange(*year, *month)?])
889 }
890 Some(Date::YearMonthDay(year, month, day)) => {
891 Ok(vec![year_month_day_to_daterange(*year, *month, *day)?])
892 }
893 None => Ok(Vec::new()),
894 }
895 }
896 "dateTime" => {
897 let fp_datetime = value
898 .as_any()
899 .downcast_ref::<FHIRDateTime>()
900 .ok_or_else(|| InsertableIndexError::FailedDowncast(value.fhir_type().to_string()))?
901 .value
902 .as_ref();
903
904 match &fp_datetime {
905 Some(date_time) => date_time_range(date_time).map(|date_range| vec![date_range]),
906 None => Ok(Vec::new()),
907 }
908 }
909 "instant" => {
910 let fp_instant = value
911 .as_any()
912 .downcast_ref::<FHIRInstant>()
913 .ok_or_else(|| {
914 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
915 })?;
916
917 match &fp_instant.value {
918 Some(Instant::Iso8601(instant)) => {
919 let timestamp = instant.timestamp_millis();
920 Ok(vec![DateRange {
921 start: timestamp,
922 end: timestamp,
923 }])
924 }
925 None => Ok(Vec::new()),
926 }
927 }
928 "Period" => {
929 let fp_period = value.as_any().downcast_ref::<Period>().ok_or_else(|| {
930 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
931 })?;
932 let fp_start = if let Some(date) = fp_period.start.as_ref() {
933 let date = date.as_ref();
934 let date_range = index_date(date)?;
935 date_range
936 .first()
937 .ok_or_else(|| {
938 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
939 })?
940 .start
941 } else {
942 0
943 };
944
945 let fp_end = if let Some(date) = fp_period.end.as_ref() {
946 let date = date.as_ref();
947 let date_range = index_date(date)?;
948 date_range
949 .first()
950 .ok_or_else(|| {
951 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
952 })?
953 .end
954 } else {
955 i64::MAX
956 };
957
958 Ok(vec![DateRange {
959 start: fp_start,
960 end: fp_end,
961 }])
962 }
963 _ => Err(InsertableIndexError::FailedDowncast(
964 value.fhir_type().to_string(),
965 )),
966 }
967}
968
969#[must_use]
972pub fn index_reference_values(value: &dyn MetaValue) -> Vec<ReferenceIndex> {
973 index_reference(value).unwrap_or_default()
974}
975
976fn index_reference(value: &dyn MetaValue) -> Result<Vec<ReferenceIndex>, InsertableIndexError> {
977 match value.fhir_type() {
978 "Reference" => {
979 let fp_reference = value.as_any().downcast_ref::<Reference>().ok_or_else(|| {
980 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
981 })?;
982
983 if let Some(reference) = &fp_reference
984 .reference
985 .as_ref()
986 .and_then(|r| r.value.as_ref())
987 {
988 let parts: Vec<&str> = reference.split('/').collect();
989 if parts.len() == 2 {
990 let resource_type = ResourceType::try_from(parts[0])?;
991 let id = parts[1].to_string();
992 return Ok(vec![ReferenceIndex {
993 resource_type: Some(resource_type.as_ref().to_string()),
994 id: Some(id),
995 uri: None,
996 }]);
997 }
998 }
999
1000 Ok(vec![])
1001 }
1002 "canonical" => {
1003 let fp_canonical = value
1004 .as_any()
1005 .downcast_ref::<FHIRCanonical>()
1006 .ok_or_else(|| {
1007 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
1008 })?;
1009 if let Some(canonical) = &fp_canonical.value {
1010 let (resource_type, id) = parse_haste_canonical(canonical)
1013 .and_then(|(resource_type, id)| {
1014 let resource_type = ResourceType::try_from(resource_type).ok()?;
1015 Some((resource_type.as_ref().to_string(), id.to_string()))
1016 })
1017 .unzip();
1018 return Ok(vec![ReferenceIndex {
1019 id,
1020 resource_type,
1021 uri: Some(canonical.clone()),
1022 }]);
1023 }
1024 Ok(vec![])
1025 }
1026 "uri" => {
1027 let fp_uri = value.as_any().downcast_ref::<FHIRUri>().ok_or_else(|| {
1028 InsertableIndexError::FailedDowncast(value.fhir_type().to_string())
1029 })?;
1030 if let Some(uri) = &fp_uri.value {
1031 return Ok(vec![ReferenceIndex {
1032 id: None,
1033 resource_type: None,
1034 uri: Some(uri.clone()),
1035 }]);
1036 }
1037 Ok(vec![])
1038 }
1039 fhir_type => {
1042 let resource_type = ResourceType::try_from(fhir_type)
1043 .map_err(|_| InsertableIndexError::FailedDowncast(fhir_type.to_string()))?;
1044 let id = value
1045 .get_field("id")
1046 .and_then(|id| id.as_any().downcast_ref::<String>());
1047 Ok(id
1048 .map(|id| ReferenceIndex {
1049 resource_type: Some(resource_type.as_ref().to_string()),
1050 id: Some(id.clone()),
1051 uri: None,
1052 })
1053 .into_iter()
1054 .collect())
1055 }
1056 }
1057}
1058
1059pub fn to_insertable_index(
1069 parameter: &ResolvedParameter,
1070 result: &[&dyn MetaValue],
1071) -> Result<InsertableIndex, OperationOutcomeError> {
1072 let search_parameter = ¶meter.search_parameter;
1073 match &search_parameter.type_ {
1074 param_type if param_type == &SearchParamType::number() => {
1075 let numbers = result
1076 .iter()
1077 .filter_map(|v| index_number(*v).ok())
1078 .flatten()
1079 .collect::<Vec<_>>();
1080 Ok(InsertableIndex::Number(numbers))
1081 }
1082 param_type if param_type == &SearchParamType::string() => {
1083 let strings = result
1084 .iter()
1085 .filter_map(|v| index_string(*v).ok())
1086 .flatten()
1087 .collect();
1088 Ok(InsertableIndex::String(strings))
1089 }
1090 param_type if param_type == &SearchParamType::uri() => {
1091 let uris = result
1092 .iter()
1093 .filter_map(|v| index_uri(*v).ok())
1094 .flatten()
1095 .collect();
1096 Ok(InsertableIndex::URI(uris))
1097 }
1098 param_type if param_type == &SearchParamType::token() => {
1099 let tokens = result
1100 .iter()
1101 .filter_map(|v| index_token(*v).ok())
1102 .flatten()
1103 .collect();
1104 Ok(InsertableIndex::Token(tokens))
1105 }
1106 param_type if param_type == &SearchParamType::date() => {
1107 let dates = result
1108 .iter()
1109 .filter_map(|v| index_date(*v).ok())
1110 .flatten()
1111 .collect();
1112 Ok(InsertableIndex::Date(dates))
1113 }
1114 param_type if param_type == &SearchParamType::reference() => {
1115 let references = result
1116 .iter()
1117 .filter_map(|v: &&dyn MetaValue| index_reference(*v).ok())
1118 .flatten()
1119 .collect();
1120 Ok(InsertableIndex::Reference(references))
1121 }
1122 param_type if param_type == &SearchParamType::quantity() => {
1123 let quantities = result
1124 .iter()
1125 .filter_map(|v| index_quantity(*v).ok())
1126 .flatten()
1127 .collect();
1128 Ok(InsertableIndex::Quantity(quantities))
1129 }
1130 param_type if param_type == &SearchParamType::composite() => {
1132 Ok(InsertableIndex::Composite(vec![]))
1133 }
1134 param_type if param_type == &SearchParamType::special() => {
1135 Ok(InsertableIndex::Special(vec![]))
1136 }
1137 _ => {
1138 let type_name = search_parameter.type_.as_str();
1139 Err(InsertableIndexError::InvalidType(
1140 type_name.map_or("unknown".to_string(), std::string::ToString::to_string),
1141 )
1142 .into())
1143 }
1144 }
1145}
1146
1147#[cfg(test)]
1148mod tests {
1149 use super::*;
1150 use haste_fhir_model::r4::generated::types::{
1151 FHIRDate, FHIRDateTime, Period, Reference, Timing,
1152 };
1153
1154 #[test]
1155 fn test_year_month_to_daterange() {
1156 let year = 2023;
1157 let month: u8 = 5;
1158 let date_range = year_month_to_daterange(year, month).unwrap();
1159
1160 assert_eq!(
1161 date_range.start,
1162 chrono::DateTime::parse_from_rfc3339("2023-05-01T00:00:00Z")
1163 .unwrap()
1164 .timestamp_millis()
1165 );
1166 assert_eq!(
1167 date_range.end,
1168 chrono::DateTime::parse_from_rfc3339("2023-05-31T23:59:59.999Z")
1169 .unwrap()
1170 .timestamp_millis()
1171 );
1172 }
1173
1174 #[test]
1175 fn test_year_month_day_to_daterange() {
1176 let year = 2023;
1177 let month: u8 = 5;
1178 let day = 15;
1179 let date_range = year_month_day_to_daterange(year, month, day).unwrap();
1180
1181 assert_eq!(
1182 date_range.start,
1183 chrono::DateTime::parse_from_rfc3339("2023-05-15T00:00:00Z")
1184 .unwrap()
1185 .timestamp_millis()
1186 );
1187 assert_eq!(
1188 date_range.end,
1189 chrono::DateTime::parse_from_rfc3339("2023-05-15T23:59:59.999Z")
1190 .unwrap()
1191 .timestamp_millis()
1192 );
1193 }
1194
1195 #[test]
1196 fn test_year_to_daterange() {
1197 let year = 2023;
1198 let date_range = year_to_daterange(year).unwrap();
1199 assert_eq!(
1200 date_range.start,
1201 chrono::DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z")
1202 .unwrap()
1203 .timestamp_millis()
1204 );
1205 assert_eq!(
1206 date_range.end,
1207 chrono::DateTime::parse_from_rfc3339("2023-12-31T23:59:59.999Z")
1208 .unwrap()
1209 .timestamp_millis()
1210 );
1211 }
1212
1213 #[test]
1214 fn test_index_date() {
1215 let date_value = FHIRDate {
1216 id: None,
1217 extension: None,
1218 value: Some(Date::Year(2023)),
1219 };
1220 let result = index_date(&date_value).unwrap();
1221 assert_eq!(result.len(), 1);
1222 assert_eq!(
1223 result[0].start,
1224 chrono::DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z")
1225 .unwrap()
1226 .timestamp_millis()
1227 );
1228 assert_eq!(
1229 result[0].end,
1230 chrono::DateTime::parse_from_rfc3339("2023-12-31T23:59:59.999Z")
1231 .unwrap()
1232 .timestamp_millis()
1233 );
1234 }
1235
1236 #[test]
1237 fn date_range_instant() {
1238 let fhir_date = FHIRDateTime {
1239 id: None,
1240 extension: None,
1241 value: Some(DateTime::Iso8601(
1242 chrono::DateTime::parse_from_rfc3339("2023-05-14T11:25:25.234-05:00")
1243 .unwrap()
1244 .with_timezone(&chrono::Utc),
1245 )),
1246 };
1247
1248 let range = index_date(&fhir_date).unwrap();
1249 let date_range = range.first().unwrap();
1250
1251 assert_eq!(
1252 date_range.start,
1253 chrono::DateTime::parse_from_rfc3339("2023-05-14T11:25:25.234-05:00")
1254 .unwrap()
1255 .with_timezone(&chrono::Utc)
1256 .timestamp_millis()
1257 );
1258 assert_eq!(
1259 date_range.end,
1260 chrono::DateTime::parse_from_rfc3339("2023-05-14T11:25:25.234-05:00")
1261 .unwrap()
1262 .with_timezone(&chrono::Utc)
1263 .timestamp_millis()
1264 );
1265 }
1266
1267 #[test]
1268 fn date_range_period() {
1269 let start = FHIRDateTime {
1270 id: None,
1271 extension: None,
1272 value: Some(DateTime::Year(2023)),
1273 };
1274
1275 let end = FHIRDateTime {
1276 id: None,
1277 extension: None,
1278 value: Some(DateTime::YearMonthDay(2023, 5, 15)),
1279 };
1280
1281 let period = Period {
1282 id: None,
1283 extension: None,
1284 start: Some(Box::new(start)),
1285 end: Some(Box::new(end)),
1286 };
1287
1288 let range = index_date(&period).unwrap();
1289 let date_range = range.first().unwrap();
1290
1291 assert_eq!(
1292 date_range.start,
1293 chrono::DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z")
1294 .unwrap()
1295 .timestamp_millis()
1296 );
1297 assert_eq!(
1298 date_range.end,
1299 chrono::DateTime::parse_from_rfc3339("2023-05-15T23:59:59.999Z")
1300 .unwrap()
1301 .timestamp_millis()
1302 );
1303 }
1304
1305 #[test]
1306 fn date_range_missing() {
1307 let start = FHIRDateTime {
1308 id: None,
1309 extension: None,
1310 value: Some(DateTime::Year(2023)),
1311 };
1312
1313 let end = FHIRDateTime {
1314 id: None,
1315 extension: None,
1316 value: Some(DateTime::YearMonthDay(2023, 5, 15)),
1317 };
1318
1319 let period = Period {
1320 id: None,
1321 extension: None,
1322 start: None,
1323 end: Some(Box::new(end)),
1324 };
1325
1326 let range = index_date(&period).unwrap();
1327 let date_range = range.first().unwrap();
1328
1329 assert_eq!(date_range.start, 0);
1330 assert_eq!(
1331 date_range.end,
1332 chrono::DateTime::parse_from_rfc3339("2023-05-15T23:59:59.999Z")
1333 .unwrap()
1334 .timestamp_millis()
1335 );
1336
1337 let period = Period {
1338 id: None,
1339 extension: None,
1340 start: Some(Box::new(start)),
1341 end: None,
1342 };
1343
1344 let range = index_date(&period).unwrap();
1345 let date_range = range.first().unwrap();
1346
1347 assert_eq!(
1348 date_range.start,
1349 chrono::DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z")
1350 .unwrap()
1351 .timestamp_millis()
1352 );
1353 assert_eq!(date_range.end, i64::MAX);
1354 }
1355
1356 #[test]
1357 fn test_date_range_end() {
1358 let year = 2023;
1359 let month: u8 = 12;
1360 let day = 31;
1361 let date_range = year_month_day_to_daterange(year, month, day).unwrap();
1362
1363 assert_eq!(
1364 date_range.start,
1365 chrono::DateTime::parse_from_rfc3339("2023-12-31T00:00:00Z")
1366 .unwrap()
1367 .timestamp_millis()
1368 );
1369
1370 assert_eq!(
1371 date_range.end,
1372 chrono::DateTime::parse_from_rfc3339("2023-12-31T23:59:59.999Z")
1373 .unwrap()
1374 .timestamp_millis()
1375 );
1376
1377 let date_range = year_month_to_daterange(year, month).unwrap();
1378
1379 assert_eq!(
1380 date_range.start,
1381 chrono::DateTime::parse_from_rfc3339("2023-12-01T00:00:00Z")
1382 .unwrap()
1383 .timestamp_millis()
1384 );
1385
1386 assert_eq!(
1387 date_range.end,
1388 chrono::DateTime::parse_from_rfc3339("2023-12-31T23:59:59.999Z")
1389 .unwrap()
1390 .timestamp_millis()
1391 );
1392 }
1393
1394 #[test]
1395 fn test_timing() {
1396 let timing = Timing {
1397 event: Some(vec![
1398 FHIRDateTime {
1399 id: None,
1400 extension: None,
1401 value: Some(DateTime::YearMonthDay(2023, 12, 31)),
1402 },
1403 FHIRDateTime {
1404 id: None,
1405 extension: None,
1406 value: Some(DateTime::YearMonthDay(2024, 1, 1)),
1407 },
1408 ]),
1409 ..Default::default()
1410 };
1411
1412 let date_ranges = index_date(&timing).unwrap();
1413 assert_eq!(date_ranges.len(), 2);
1414
1415 assert_eq!(
1416 date_ranges[0],
1417 DateRange {
1418 start: chrono::DateTime::parse_from_rfc3339("2023-12-31T00:00:00Z")
1419 .unwrap()
1420 .timestamp_millis(),
1421 end: chrono::DateTime::parse_from_rfc3339("2023-12-31T23:59:59.999Z")
1422 .unwrap()
1423 .timestamp_millis(),
1424 }
1425 );
1426
1427 assert_eq!(
1428 date_ranges[1],
1429 DateRange {
1430 start: chrono::DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z")
1431 .unwrap()
1432 .timestamp_millis(),
1433 end: chrono::DateTime::parse_from_rfc3339("2024-01-01T23:59:59.999Z")
1434 .unwrap()
1435 .timestamp_millis(),
1436 }
1437 );
1438 }
1439
1440 #[test]
1441 fn test_indexing_reference() {
1442 let reference = Reference {
1443 type_: None,
1444 identifier_: None,
1445 display: None,
1446 id: None,
1447 extension: None,
1448 reference: Some(Box::new(FHIRString {
1449 id: None,
1450 extension: None,
1451 value: Some("Patient/123".to_string()),
1452 })),
1453 };
1454
1455 let result = index_reference(&reference).unwrap();
1456 assert_eq!(result.len(), 1);
1457 assert_eq!(result[0].resource_type, Some("Patient".to_string()));
1458 assert_eq!(result[0].id, Some("123".to_string()));
1459 assert!(result[0].uri.is_none());
1460 }
1461
1462 #[test]
1463 fn test_indexing_invalid_reference() {
1464 let reference = Reference {
1465 type_: None,
1466 identifier_: None,
1467 display: None,
1468 id: None,
1469 extension: None,
1470 reference: Some(Box::new(FHIRString {
1471 id: None,
1472 extension: None,
1473 value: Some("BadType/123".to_string()),
1474 })),
1475 };
1476
1477 let result = index_reference(&reference);
1478
1479 assert!(result.is_err());
1480 }
1481
1482 #[test]
1483 fn test_canonical_index() {
1484 let canonical = FHIRCanonical {
1485 id: None,
1486 extension: None,
1487 value: Some("http://example.com/CanonicalResource".to_string()),
1488 };
1489 let result = index_reference(&canonical).unwrap();
1490 assert_eq!(result.len(), 1);
1491 assert!(result[0].id.is_none());
1492 assert!(result[0].resource_type.is_none());
1493 assert_eq!(
1494 result[0].uri,
1495 Some("http://example.com/CanonicalResource".to_string())
1496 );
1497 }
1498
1499 #[test]
1500 fn test_uri_indexing() {
1501 let uri = FHIRUri {
1502 id: None,
1503 extension: None,
1504 value: Some("http://example.com/URIResource".to_string()),
1505 };
1506 let result = index_reference(&uri).unwrap();
1507 assert_eq!(result.len(), 1);
1508 assert!(result[0].id.is_none());
1509 assert!(result[0].resource_type.is_none());
1510 assert_eq!(
1511 result[0].uri,
1512 Some("http://example.com/URIResource".to_string())
1513 );
1514 }
1515}