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