1mod error;
2mod parser;
3use crate::{
4 error::{FunctionError, OperationError},
5 parser::{
6 Expression, FunctionInvocation, Identifier, Invocation, Literal, Operation,
7 QualifiedIdentifier, Term,
8 },
9};
10use dashmap::DashMap;
11pub use error::FHIRPathError;
12use haste_fhir_model::r4::{
13 conversion::{
14 BOOLEAN_TYPES, NUMBER_TYPES, STRING_TYPES, downcast_bool, downcast_number, downcast_string,
15 },
16 generated::{
17 resources::ResourceType,
18 types::{FHIRBoolean, FHIRDecimal, FHIRId, FHIRInteger, FHIRString, Reference},
19 },
20};
21use haste_reflect::MetaValue;
22use haste_reflect_derive::Reflect;
23use std::pin::Pin;
24use std::{
25 collections::HashMap,
26 sync::{Arc, LazyLock},
27};
28use tokio::sync::Mutex;
29
30mod allocators;
31use allocators::AllocatorTrait;
32
33async fn evaluate_literal<'b>(
34 literal: &Literal,
35 context: Context<'b>,
36) -> Result<Context<'b>, FHIRPathError> {
37 match literal {
38 Literal::String(string) => Ok(context.new_context_from(vec![
39 context
40 .allocate_literal(FHIRString {
41 value: Some(string.clone()),
42 ..Default::default()
43 })
44 .await,
45 ])),
46 Literal::Integer(int) => Ok(context.new_context_from(vec![
47 context
48 .allocate_literal(FHIRInteger {
49 value: Some(*int),
50 ..Default::default()
51 })
52 .await,
53 ])),
54 Literal::Float(decimal) => Ok(context.new_context_from(vec![
55 context
56 .allocate_literal(FHIRDecimal {
57 value: Some(*decimal),
58 ..Default::default()
59 })
60 .await,
61 ])),
62 Literal::Boolean(bool) => Ok(context.new_context_from(vec![
63 context
64 .allocate_literal(FHIRBoolean {
65 value: Some(*bool),
66 ..Default::default()
67 })
68 .await,
69 ])),
70 Literal::Null => Ok(context.new_context_from(vec![])),
71 _ => Err(FHIRPathError::InvalidLiteral(literal.to_owned())),
72 }
73}
74
75async fn evaluate_invocation<'a>(
76 invocation: &Invocation,
77 context: Context<'a>,
78 config: Option<Arc<Config<'a>>>,
79) -> Result<Context<'a>, FHIRPathError> {
80 match invocation {
81 Invocation::This => Ok(context),
82 Invocation::Index(index_expression) => {
83 let index = evaluate_expression(index_expression, context.clone(), config).await?;
84 if index.values.len() != 1 {
85 return Err(FHIRPathError::OperationError(
86 OperationError::InvalidCardinality,
87 ));
88 }
89
90 let float_index = downcast_number(index.values[0])?;
91
92 if float_index < 0.0 || float_index.fract() != 0.0 {
94 return Err(FHIRPathError::OperationError(OperationError::InvalidIndex));
95 }
96
97 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
100 let index: usize = (float_index as u64)
101 .try_into()
102 .map_err(|_| FHIRPathError::OperationError(OperationError::IndexOutOfBounds))?;
103
104 if let Some(value) = context.values.get(index) {
105 Ok(context.new_context_from(vec![*value]))
106 } else {
107 Ok(context.new_context_from(vec![]))
108 }
109 }
110 Invocation::IndexAccessor => Err(FHIRPathError::NotImplemented("index access".to_string())),
111 Invocation::Total => Err(FHIRPathError::NotImplemented("total".to_string())),
112 Invocation::Identifier(Identifier(id)) => Ok(context.new_context_from(
113 context
114 .values
115 .iter()
116 .flat_map(|v| v.get_field(id).map_or_else(Vec::new, |v| v.flatten()))
117 .collect(),
118 )),
119 Invocation::Function(function) => evaluate_function(function, context, config).await,
120 }
121}
122
123async fn evaluate_term<'a>(
124 term: &Term,
125 context: Context<'a>,
126 config: Option<Arc<Config<'a>>>,
127) -> Result<Context<'a>, FHIRPathError> {
128 match term {
129 Term::Literal(literal) => evaluate_literal(literal, context).await,
130 Term::ExternalConstant(constant) => {
131 resolve_external_constant(
132 constant,
133 config.as_ref().and_then(|c| c.variable_resolver.as_ref()),
134 context,
135 )
136 .await
137 }
138 Term::Parenthesized(expression) => evaluate_expression(expression, context, config).await,
139 Term::Invocation(invocation) => evaluate_invocation(invocation, context, config).await,
140 }
141}
142
143async fn evaluate_first_term<'a>(
146 term: &Term,
147 context: Context<'a>,
148 config: Option<Arc<Config<'a>>>,
149) -> Result<Context<'a>, FHIRPathError> {
150 match term {
151 Term::Invocation(invocation) => match invocation {
152 Invocation::Identifier(identifier) => {
153 let type_filter = filter_by_type(&identifier.0, &context);
154 if type_filter.values.is_empty() {
155 evaluate_invocation(invocation, context, config).await
156 } else {
157 Ok(type_filter)
158 }
159 }
160 _ => evaluate_invocation(invocation, context, config).await,
161 },
162 _ => evaluate_term(term, context, config).await,
163 }
164}
165
166async fn evaluate_singular<'a>(
167 expression: &[Term],
168 context: Context<'a>,
169 config: Option<Arc<Config<'a>>>,
170) -> Result<Context<'a>, FHIRPathError> {
171 let mut current_context = context;
172
173 let mut term_iterator = expression.iter();
174 let first_term = term_iterator.next();
175 if let Some(first_term) = first_term {
176 current_context = evaluate_first_term(first_term, current_context, config.clone()).await?;
177 }
178
179 for term in term_iterator {
180 current_context = evaluate_term(term, current_context, config.clone()).await?;
181 }
182
183 Ok(current_context)
184}
185
186async fn operation_2<'a>(
187 left: &Expression,
188 right: &Expression,
189 context: Context<'a>,
190 config: Option<Arc<Config<'a>>>,
191 executor: impl Fn(
192 Context<'a>,
193 Context<'a>,
194 )
195 -> Pin<Box<dyn Future<Output = Result<Context<'a>, FHIRPathError>> + Send + 'a>>,
196) -> Result<Context<'a>, FHIRPathError> {
197 let left = evaluate_expression(left, context.clone(), config.clone()).await?;
198 let right = evaluate_expression(right, context, config).await?;
199
200 if left.values.is_empty() || right.values.is_empty() {
202 return Ok(left.new_context_from(vec![]));
203 }
204
205 if left.values.len() != 1 || right.values.len() != 1 {
206 return Err(FHIRPathError::OperationError(
207 OperationError::InvalidCardinality,
208 ));
209 }
210
211 executor(left, right).await
212}
213
214async fn operation_n<'a>(
215 left: &Expression,
216 right: &Expression,
217 context: Context<'a>,
218 config: Option<Arc<Config<'a>>>,
219 executor: impl Fn(Context<'a>, Context<'a>) -> Result<Context<'a>, FHIRPathError>,
220) -> Result<Context<'a>, FHIRPathError> {
221 let left = evaluate_expression(left, context.clone(), config.clone()).await?;
222 let right = evaluate_expression(right, context, config).await?;
223 executor(left, right)
224}
225
226enum Cardinality {
227 Zero,
228 One,
229 Many,
230 Custom(usize, usize),
231}
232
233fn validate_arguments(
234 ast_arguments: &[Expression],
235 cardinality: &Cardinality,
236) -> Result<(), FHIRPathError> {
237 match cardinality {
238 Cardinality::Zero => {
239 if !ast_arguments.is_empty() {
240 return Err(FHIRPathError::OperationError(
241 OperationError::InvalidCardinality,
242 ));
243 }
244 }
245 Cardinality::Custom(min, max) => {
246 if ast_arguments.len() < *min || ast_arguments.len() > *max {
247 return Err(FHIRPathError::OperationError(
248 OperationError::InvalidCardinality,
249 ));
250 }
251 }
252 Cardinality::One => {
253 if ast_arguments.len() != 1 {
254 return Err(FHIRPathError::OperationError(
255 OperationError::InvalidCardinality,
256 ));
257 }
258 }
259 Cardinality::Many => {}
260 }
261 Ok(())
262}
263
264fn derive_typename(expression_ast: &Expression) -> Result<String, FHIRPathError> {
265 match expression_ast {
266 Expression::Singular(ast) => match &ast[0] {
267 Term::Invocation(Invocation::Identifier(type_id)) => Ok(type_id.0.clone()),
268 _ => Err(FHIRPathError::FailedTypeNameDerivation),
269 },
270 Expression::Operation(_) => Err(FHIRPathError::FailedTypeNameDerivation),
271 }
272}
273
274fn check_type_name(type_name: &str, type_to_check: &str) -> bool {
275 match type_to_check {
276 "Resource" | "DomainResource" => ResourceType::try_from(type_name).is_ok(),
277 _ => type_name == type_to_check,
278 }
279}
280
281fn check_type(value: &dyn MetaValue, type_to_check: &str) -> bool {
282 let fhir_type_name = value.fhir_type();
283
284 match fhir_type_name {
285 "Reference" => {
287 if type_to_check == "Reference" {
288 return true;
289 } else if let Some(reference) = value.as_any().downcast_ref::<Reference>()
290 && let Some(resource_type) = reference
291 .reference
292 .as_ref()
293 .and_then(|r| r.value.as_ref())
294 .and_then(|r| r.split('/').next())
295 {
296 return check_type_name(resource_type, type_to_check);
297 }
298 false
299 }
300 fhir_type_name => check_type_name(fhir_type_name, type_to_check),
301 }
302}
303
304fn filter_by_type<'a>(type_name: &str, context: &Context<'a>) -> Context<'a> {
305 context.new_context_from(
306 context
307 .values
308 .iter()
309 .filter(|v| check_type(**v, type_name))
310 .copied()
311 .collect(),
312 )
313}
314
315#[derive(Debug, Reflect)]
316#[fhir_type = "Element"]
317struct Reflection {
318 name: String,
319}
320
321async fn evaluate_function<'a>(
322 function: &FunctionInvocation,
323 context: Context<'a>,
324 config: Option<Arc<Config<'a>>>,
325) -> Result<Context<'a>, FHIRPathError> {
326 match function.name.0.as_str() {
327 "resolve" => Ok(context),
328 "where" => evaluate_where(function, context, config).await,
329 "ofType" | "as" => evaluate_of_type(function, &context),
330 "count" => evaluate_count(function, context).await,
331 "upper" | "lower" => evaluate_case(function, context).await,
332 "empty" => evaluate_empty(function, context).await,
333 "join" => evaluate_join(function, context, config).await,
334 "exists" => evaluate_exists(function, context, config).await,
335 "children" => evaluate_children(function, &context),
336 "repeat" => evaluate_repeat(function, context, config).await,
337 "descendants" => evaluate_descendants(context, config).await,
338 "type" => evaluate_type(function, context).await,
339 "first" => evaluate_first(function, &context),
340 "getReferenceKey" => evaluate_get_reference_key(function, context).await,
341 "getResourceKey" => evaluate_get_resource_key(function, context, config).await,
342
343 _ => Err(FHIRPathError::NotImplemented(format!(
344 "Function '{}' is not implemented",
345 function.name.0
346 ))),
347 }
348}
349
350async fn evaluate_where<'a>(
351 function: &FunctionInvocation,
352 context: Context<'a>,
353 config: Option<Arc<Config<'a>>>,
354) -> Result<Context<'a>, FHIRPathError> {
355 validate_arguments(&function.arguments, &Cardinality::One)?;
356
357 let where_condition = &function.arguments[0];
358 let mut new_context = vec![];
359
360 for value in &context.values {
361 let result = evaluate_expression(
362 where_condition,
363 context.new_context_from(vec![*value]),
364 config.clone(),
365 )
366 .await?;
367
368 if result.values.len() > 1 {
369 return Err(FHIRPathError::InternalError(
370 "Where condition did not return a single value".to_string(),
371 ));
372 }
373
374 if !result.values.is_empty() && downcast_bool(result.values[0])? {
376 new_context.push(*value);
377 }
378 }
379
380 Ok(context.new_context_from(new_context))
381}
382
383fn evaluate_of_type<'a>(
384 function: &FunctionInvocation,
385 context: &Context<'a>,
386) -> Result<Context<'a>, FHIRPathError> {
387 validate_arguments(&function.arguments, &Cardinality::One)?;
388
389 let type_name = derive_typename(&function.arguments[0])?;
390
391 Ok(filter_by_type(&type_name, context))
392}
393
394async fn evaluate_count<'a>(
395 function: &FunctionInvocation,
396 context: Context<'a>,
397) -> Result<Context<'a>, FHIRPathError> {
398 validate_arguments(&function.arguments, &Cardinality::Zero)?;
399
400 let count: i64 = context
401 .values
402 .len()
403 .try_into()
404 .map_err(|_| FHIRPathError::OperationError(OperationError::SizeOverflow))?;
405
406 Ok(context.new_context_from(vec![
407 context
408 .allocate_literal(FHIRInteger {
409 value: Some(count),
410 ..Default::default()
411 })
412 .await,
413 ]))
414}
415
416async fn evaluate_case<'a>(
417 function: &FunctionInvocation,
418 context: Context<'a>,
419) -> Result<Context<'a>, FHIRPathError> {
420 validate_arguments(&function.arguments, &Cardinality::Zero)?;
421
422 let op = function.name.0.as_str();
423
424 if context.values.is_empty() {
425 return Ok(context.new_context_from(vec![]));
426 }
427
428 if context.values.len() > 1 {
429 return Err(FunctionError::InvalidCardinality(op.to_string(), context.values.len()).into());
430 }
431
432 let input = downcast_string(context.values[0])?;
433
434 let transformed = match op {
435 "upper" => input.to_uppercase(),
436 "lower" => input.to_lowercase(),
437 _ => unreachable!(),
438 };
439
440 Ok(context.new_context_from(vec![
441 context
442 .allocate_literal(FHIRString {
443 value: Some(transformed),
444 ..Default::default()
445 })
446 .await,
447 ]))
448}
449
450async fn evaluate_empty<'a>(
451 function: &FunctionInvocation,
452 context: Context<'a>,
453) -> Result<Context<'a>, FHIRPathError> {
454 validate_arguments(&function.arguments, &Cardinality::Zero)?;
455
456 Ok(context.new_context_from(vec![
457 context
458 .allocate_literal(FHIRBoolean {
459 value: Some(context.values.is_empty()),
460 ..Default::default()
461 })
462 .await,
463 ]))
464}
465
466async fn evaluate_join<'a>(
467 function: &FunctionInvocation,
468 context: Context<'a>,
469 config: Option<Arc<Config<'a>>>,
470) -> Result<Context<'a>, FHIRPathError> {
471 validate_arguments(&function.arguments, &Cardinality::Custom(0, 1))?;
472
473 let separator = if let Some(separator_expression) = function.arguments.first() {
474 let separator_context =
475 evaluate_expression(separator_expression, context.clone(), config).await?;
476
477 if separator_context.values.len() != 1 {
478 return Err(FHIRPathError::OperationError(
479 OperationError::InvalidCardinality,
480 ));
481 }
482
483 downcast_string(separator_context.values[0])?
484 } else {
485 String::new()
486 };
487
488 let joined = context
489 .values
490 .iter()
491 .map(|v| downcast_string(*v))
492 .collect::<Result<Vec<_>, _>>()?
493 .join(&separator);
494
495 Ok(context.new_context_from(vec![
496 context
497 .allocate_literal(FHIRString {
498 value: Some(joined),
499 ..Default::default()
500 })
501 .await,
502 ]))
503}
504
505async fn evaluate_exists<'a>(
506 function: &FunctionInvocation,
507 context: Context<'a>,
508 config: Option<Arc<Config<'a>>>,
509) -> Result<Context<'a>, FHIRPathError> {
510 validate_arguments(&function.arguments, &Cardinality::Many)?;
511
512 if function.arguments.len() > 1 {
513 return Err(FunctionError::InvalidCardinality(
514 "exists".to_string(),
515 function.arguments.len(),
516 )
517 .into());
518 }
519
520 if let Some(condition) = function.arguments.first() {
521 for value in &context.values {
522 let result = evaluate_expression(
523 condition,
524 context.new_context_from(vec![*value]),
525 config.clone(),
526 )
527 .await?;
528
529 if result.values.len() > 1 {
530 return Err(FHIRPathError::InternalError(
531 "Exists condition did not return a single value".to_string(),
532 ));
533 }
534
535 if !result.values.is_empty() && downcast_bool(result.values[0])? {
536 return Ok(context.new_context_from(vec![
537 context
538 .allocate_literal(FHIRBoolean {
539 value: Some(true),
540 ..Default::default()
541 })
542 .await,
543 ]));
544 }
545 }
546
547 return Ok(context.new_context_from(vec![
548 context
549 .allocate_literal(FHIRBoolean {
550 value: Some(false),
551 ..Default::default()
552 })
553 .await,
554 ]));
555 }
556
557 Ok(context.new_context_from(vec![
558 context
559 .allocate_literal(FHIRBoolean {
560 value: Some(!context.values.is_empty()),
561 ..Default::default()
562 })
563 .await,
564 ]))
565}
566
567fn evaluate_children<'a>(
568 function: &FunctionInvocation,
569 context: &Context<'a>,
570) -> Result<Context<'a>, FHIRPathError> {
571 validate_arguments(&function.arguments, &Cardinality::Zero)?;
572
573 let children = context
574 .values
575 .iter()
576 .flat_map(|value| {
577 value
578 .fields()
579 .iter()
580 .filter_map(|f| value.get_field(f).map(|v| v.flatten()))
581 .flatten()
582 .collect::<Vec<_>>()
583 })
584 .collect();
585
586 Ok(context.new_context_from(children))
587}
588
589async fn evaluate_repeat<'a>(
590 function: &FunctionInvocation,
591 context: Context<'a>,
592 config: Option<Arc<Config<'a>>>,
593) -> Result<Context<'a>, FHIRPathError> {
594 validate_arguments(&function.arguments, &Cardinality::One)?;
595
596 let projection = &function.arguments[0];
597 let mut end_result = vec![];
598 let mut cur = context;
599
600 while !cur.values.is_empty() {
601 cur = evaluate_expression(projection, cur, config.clone()).await?;
602 end_result.extend_from_slice(cur.values.as_slice());
603 }
604
605 Ok(cur.new_context_from(end_result))
606}
607
608async fn evaluate_descendants<'a>(
609 context: Context<'a>,
610 config: Option<Arc<Config<'a>>>,
611) -> Result<Context<'a>, FHIRPathError> {
612 let result = evaluate_expression(
613 &Expression::Singular(vec![Term::Invocation(Invocation::Function(
614 FunctionInvocation {
615 name: Identifier("repeat".to_string()),
616 arguments: vec![Expression::Singular(vec![Term::Invocation(
617 Invocation::Function(FunctionInvocation {
618 name: Identifier("children".to_string()),
619 arguments: vec![],
620 }),
621 )])],
622 },
623 ))]),
624 context,
625 config,
626 )
627 .await?;
628
629 Ok(result)
630}
631
632async fn evaluate_type<'a>(
633 function: &FunctionInvocation,
634 context: Context<'a>,
635) -> Result<Context<'a>, FHIRPathError> {
636 validate_arguments(&function.arguments, &Cardinality::Zero)?;
637
638 let mut next_ctx = Vec::with_capacity(context.values.len());
639
640 for value in &context.values {
641 let type_name = value.fhir_type();
642
643 next_ctx.push(
644 context
645 .allocate_literal(Reflection {
646 name: type_name.to_string(),
647 })
648 .await,
649 );
650 }
651
652 Ok(context.new_context_from(next_ctx))
653}
654
655fn evaluate_first<'a>(
656 function: &FunctionInvocation,
657 context: &Context<'a>,
658) -> Result<Context<'a>, FHIRPathError> {
659 validate_arguments(&function.arguments, &Cardinality::Zero)?;
660
661 match context.values.first() {
662 Some(value) => Ok(context.new_context_from(vec![*value])),
663 None => Ok(context.new_context_from(vec![])),
664 }
665}
666
667async fn evaluate_get_reference_key<'a>(
668 function: &FunctionInvocation,
669 context: Context<'a>,
670) -> Result<Context<'a>, FHIRPathError> {
671 validate_arguments(&function.arguments, &Cardinality::Custom(0, 1))?;
672
673 let type_to_filter_by = if let Some(resource_type) = function.arguments.first() {
674 Some(derive_typename(resource_type)?)
675 } else {
676 None
677 };
678
679 let ids = context
680 .iter()
681 .filter_map(|value| {
682 let reference = value.as_any().downcast_ref::<Reference>()?;
683
684 let mut pieces = reference
685 .reference
686 .as_ref()
687 .and_then(|r| r.value.as_ref())
688 .map(|r| r.split('/'))?;
689
690 let resource_type = pieces.next()?;
691 let id = pieces.next()?;
692
693 if let Some(type_to_filter_by) = &type_to_filter_by
694 && !check_type_name(resource_type, type_to_filter_by)
695 {
696 return None;
697 }
698
699 Some(FHIRId {
700 value: Some(id.to_string()),
701 ..Default::default()
702 })
703 })
704 .collect::<Vec<_>>();
705
706 let mut next_context = Vec::with_capacity(ids.len());
707
708 for id in ids {
709 next_context.push(context.allocate_literal(id).await);
710 }
711
712 Ok(context.new_context_from(next_context))
713}
714
715async fn evaluate_get_resource_key<'a>(
716 function: &FunctionInvocation,
717 context: Context<'a>,
718 config: Option<Arc<Config<'a>>>,
719) -> Result<Context<'a>, FHIRPathError> {
720 validate_arguments(&function.arguments, &Cardinality::Zero)?;
721
722 let Some(id) = config.and_then(|c| c.resource_id.clone()) else {
723 return Err(FHIRPathError::InternalError(
724 "getResourceKey function requires resource_id in config".to_string(),
725 ));
726 };
727
728 let resource_key = FHIRId {
729 value: Some(id),
730 ..Default::default()
731 };
732
733 Ok(context.new_context_from(vec![context.allocate_literal(resource_key).await]))
734}
735
736fn equal_check<'b>(left: &Context<'b>, right: &Context<'b>) -> Result<bool, FHIRPathError> {
737 if NUMBER_TYPES.contains(left.values[0].fhir_type())
738 && NUMBER_TYPES.contains(right.values[0].fhir_type())
739 {
740 let left_value = downcast_number(left.values[0])?;
741 let right_value = downcast_number(right.values[0])?;
742 #[allow(clippy::float_cmp)]
743 Ok(left_value == right_value)
744 } else if STRING_TYPES.contains(left.values[0].fhir_type())
745 && STRING_TYPES.contains(right.values[0].fhir_type())
746 {
747 let left_value = downcast_string(left.values[0])?;
748 let right_value = downcast_string(right.values[0])?;
749 Ok(left_value == right_value)
750 } else if BOOLEAN_TYPES.contains(left.values[0].fhir_type())
751 && BOOLEAN_TYPES.contains(right.values[0].fhir_type())
752 {
753 let left_value = downcast_bool(left.values[0])?;
754 let right_value = downcast_bool(right.values[0])?;
755 #[allow(clippy::float_cmp)]
758 Ok(left_value == right_value)
759 } else {
760 Ok(false)
767 }
768}
769
770async fn evaluate_operation<'a>(
771 operation: &Operation,
772 context: Context<'a>,
773 config: Option<Arc<Config<'a>>>,
774) -> Result<Context<'a>, FHIRPathError> {
775 match operation {
776 Operation::Add(left, right) => evaluate_add(left, right, context, config).await,
777 Operation::Subtraction(left, right) => {
778 evaluate_subtraction(left, right, context, config).await
779 }
780 Operation::Multiplication(left, right) => {
781 evaluate_multiplication(left, right, context, config).await
782 }
783 Operation::Division(left, right) => evaluate_division(left, right, context, config).await,
784 Operation::Equal(left, right) => evaluate_equal(left, right, context, config).await,
785 Operation::NotEqual(left, right) => evaluate_not_equal(left, right, context, config).await,
786 Operation::And(left, right) => evaluate_and(left, right, context, config).await,
787 Operation::Or(left, right) => evaluate_or(left, right, context, config).await,
788 Operation::Union(left, right) => evaluate_union(left, right, context, config).await,
789 Operation::Is(expr, ty) => evaluate_is(expr, ty, context, config).await,
790 Operation::As(expr, ty) => evaluate_as(expr, ty, context, config).await,
791 Operation::XOr(left, right) => evaluate_xor(left, right, context, config).await,
792
793 Operation::Modulo(_, _) => not_implemented("Modulo"),
794 Operation::Polarity(_, _) => not_implemented("Polarity"),
795 Operation::DivisionTruncated(_, _) => not_implemented("DivisionTruncated"),
796 Operation::LessThan(left, right) => evaluate_less_than(left, right, context, config).await,
797 Operation::GreaterThan(left, right) => {
798 evaluate_greater_than(left, right, context, config).await
799 }
800 Operation::LessThanEqual(left, right) => {
801 evaluate_less_than_equal(left, right, context, config).await
802 }
803 Operation::GreaterThanEqual(left, right) => {
804 evaluate_greater_than_equal(left, right, context, config).await
805 }
806 Operation::Equivalent(_, _) => not_implemented("Equivalent"),
807 Operation::NotEquivalent(_, _) => not_implemented("NotEquivalent"),
808 Operation::In(_, _) => not_implemented("In"),
809 Operation::Contains(_, _) => not_implemented("Contains"),
810 Operation::Implies(_, _) => not_implemented("Implies"),
811 }
812}
813
814fn not_implemented(name: &'static str) -> Result<Context<'static>, FHIRPathError> {
815 Err(FHIRPathError::NotImplemented(name.to_string()))
816}
817
818async fn evaluate_add<'a>(
819 left: &Expression,
820 right: &Expression,
821 context: Context<'a>,
822 config: Option<Arc<Config<'a>>>,
823) -> Result<Context<'a>, FHIRPathError> {
824 operation_2(left, right, context, config, |left, right| {
825 Box::pin(async move {
826 if NUMBER_TYPES.contains(left.values[0].fhir_type())
827 && NUMBER_TYPES.contains(right.values[0].fhir_type())
828 {
829 let left_value = downcast_number(left.values[0])?;
830 let right_value = downcast_number(right.values[0])?;
831
832 Ok(left.new_context_from(vec![
833 left.allocate_literal(FHIRDecimal {
834 value: Some(left_value + right_value),
835 ..Default::default()
836 })
837 .await,
838 ]))
839 } else if STRING_TYPES.contains(left.values[0].fhir_type())
840 && STRING_TYPES.contains(right.values[0].fhir_type())
841 {
842 let left_string = downcast_string(left.values[0])?;
843 let right_string = downcast_string(right.values[0])?;
844
845 Ok(left.new_context_from(vec![
846 left.allocate_literal(FHIRString {
847 value: Some(left_string + &right_string),
848 ..Default::default()
849 })
850 .await,
851 ]))
852 } else {
853 Err(FHIRPathError::OperationError(OperationError::TypeMismatch(
854 left.values[0].fhir_type(),
855 right.values[0].fhir_type(),
856 )))
857 }
858 })
859 })
860 .await
861}
862
863async fn evaluate_numeric_binary<'a, F>(
864 left: &Expression,
865 right: &Expression,
866 context: Context<'a>,
867 config: Option<Arc<Config<'a>>>,
868 op: F,
869) -> Result<Context<'a>, FHIRPathError>
870where
871 F: FnOnce(f64, f64) -> f64 + Copy + Send + 'static,
872{
873 operation_2(left, right, context, config, move |left, right| {
874 Box::pin(async move {
875 let left_value = downcast_number(left.values[0])?;
876 let right_value = downcast_number(right.values[0])?;
877
878 Ok(left.new_context_from(vec![
879 left.allocate_literal(FHIRDecimal {
880 value: Some(op(left_value, right_value)),
881 ..Default::default()
882 })
883 .await,
884 ]))
885 })
886 })
887 .await
888}
889
890async fn evaluate_numerical_comparison<'a, F>(
891 left: &Expression,
892 right: &Expression,
893 context: Context<'a>,
894 config: Option<Arc<Config<'a>>>,
895 op: F,
896) -> Result<Context<'a>, FHIRPathError>
897where
898 F: FnOnce(f64, f64) -> bool + Copy + Send + 'static,
899{
900 operation_2(left, right, context, config, move |left, right| {
901 Box::pin(async move {
902 let left_value = downcast_number(left.values[0])?;
903 let right_value = downcast_number(right.values[0])?;
904
905 Ok(left.new_context_from(vec![
906 left.allocate_literal(FHIRBoolean {
907 value: Some(op(left_value, right_value)),
908 ..Default::default()
909 })
910 .await,
911 ]))
912 })
913 })
914 .await
915}
916async fn evaluate_subtraction<'a>(
917 left: &Expression,
918 right: &Expression,
919 context: Context<'a>,
920 config: Option<Arc<Config<'a>>>,
921) -> Result<Context<'a>, FHIRPathError> {
922 evaluate_numeric_binary(left, right, context, config, |l, r| l - r).await
923}
924
925async fn evaluate_multiplication<'a>(
926 left: &Expression,
927 right: &Expression,
928 context: Context<'a>,
929 config: Option<Arc<Config<'a>>>,
930) -> Result<Context<'a>, FHIRPathError> {
931 evaluate_numeric_binary(left, right, context, config, |l, r| l * r).await
932}
933
934async fn evaluate_division<'a>(
935 left: &Expression,
936 right: &Expression,
937 context: Context<'a>,
938 config: Option<Arc<Config<'a>>>,
939) -> Result<Context<'a>, FHIRPathError> {
940 evaluate_numeric_binary(left, right, context, config, |l, r| l / r).await
941}
942
943async fn evaluate_less_than<'a>(
944 left: &Expression,
945 right: &Expression,
946 context: Context<'a>,
947 config: Option<Arc<Config<'a>>>,
948) -> Result<Context<'a>, FHIRPathError> {
949 evaluate_numerical_comparison(left, right, context, config, |l, r| l < r).await
950}
951
952async fn evaluate_less_than_equal<'a>(
953 left: &Expression,
954 right: &Expression,
955 context: Context<'a>,
956 config: Option<Arc<Config<'a>>>,
957) -> Result<Context<'a>, FHIRPathError> {
958 evaluate_numerical_comparison(left, right, context, config, |l, r| l <= r).await
959}
960
961async fn evaluate_greater_than<'a>(
962 left: &Expression,
963 right: &Expression,
964 context: Context<'a>,
965 config: Option<Arc<Config<'a>>>,
966) -> Result<Context<'a>, FHIRPathError> {
967 evaluate_numerical_comparison(left, right, context, config, |l, r| l > r).await
968}
969
970async fn evaluate_greater_than_equal<'a>(
971 left: &Expression,
972 right: &Expression,
973 context: Context<'a>,
974 config: Option<Arc<Config<'a>>>,
975) -> Result<Context<'a>, FHIRPathError> {
976 evaluate_numerical_comparison(left, right, context, config, |l, r| l >= r).await
977}
978
979async fn evaluate_boolean_binary<'a, F>(
980 left: &Expression,
981 right: &Expression,
982 context: Context<'a>,
983 config: Option<Arc<Config<'a>>>,
984 op: F,
985) -> Result<Context<'a>, FHIRPathError>
986where
987 F: FnOnce(bool, bool) -> bool + Copy + Send + 'static,
988{
989 operation_2(left, right, context, config, move |left, right| {
990 Box::pin(async move {
991 let l = downcast_bool(left.values[0])?;
992 let r = downcast_bool(right.values[0])?;
993
994 Ok(left.new_context_from(vec![
995 left.allocate_literal(FHIRBoolean {
996 value: Some(op(l, r)),
997 ..Default::default()
998 })
999 .await,
1000 ]))
1001 })
1002 })
1003 .await
1004}
1005
1006async fn evaluate_and<'a>(
1007 left: &Expression,
1008 right: &Expression,
1009 context: Context<'a>,
1010 config: Option<Arc<Config<'a>>>,
1011) -> Result<Context<'a>, FHIRPathError> {
1012 evaluate_boolean_binary(left, right, context, config, |l, r| l && r).await
1013}
1014
1015async fn evaluate_or<'a>(
1016 left: &Expression,
1017 right: &Expression,
1018 context: Context<'a>,
1019 config: Option<Arc<Config<'a>>>,
1020) -> Result<Context<'a>, FHIRPathError> {
1021 evaluate_boolean_binary(left, right, context, config, |l, r| l || r).await
1022}
1023
1024async fn evaluate_xor<'a>(
1025 left: &Expression,
1026 right: &Expression,
1027 context: Context<'a>,
1028 config: Option<Arc<Config<'a>>>,
1029) -> Result<Context<'a>, FHIRPathError> {
1030 evaluate_boolean_binary(left, right, context, config, |l, r| l ^ r).await
1031}
1032
1033async fn evaluate_equality<'a>(
1034 left: &Expression,
1035 right: &Expression,
1036 context: Context<'a>,
1037 config: Option<Arc<Config<'a>>>,
1038 negate: bool,
1039) -> Result<Context<'a>, FHIRPathError> {
1040 operation_2(left, right, context, config, move |left, right| {
1041 Box::pin(async move {
1042 let mut result = equal_check(&left, &right)?;
1043
1044 if negate {
1045 result = !result;
1046 }
1047
1048 Ok(left.new_context_from(vec![
1049 left.allocate_literal(FHIRBoolean {
1050 value: Some(result),
1051 ..Default::default()
1052 })
1053 .await,
1054 ]))
1055 })
1056 })
1057 .await
1058}
1059
1060async fn evaluate_equal<'a>(
1061 left: &Expression,
1062 right: &Expression,
1063 context: Context<'a>,
1064 config: Option<Arc<Config<'a>>>,
1065) -> Result<Context<'a>, FHIRPathError> {
1066 evaluate_equality(left, right, context, config, false).await
1067}
1068
1069async fn evaluate_not_equal<'a>(
1070 left: &Expression,
1071 right: &Expression,
1072 context: Context<'a>,
1073 config: Option<Arc<Config<'a>>>,
1074) -> Result<Context<'a>, FHIRPathError> {
1075 evaluate_equality(left, right, context, config, true).await
1076}
1077
1078async fn evaluate_union<'a>(
1079 left: &Expression,
1080 right: &Expression,
1081 context: Context<'a>,
1082 config: Option<Arc<Config<'a>>>,
1083) -> Result<Context<'a>, FHIRPathError> {
1084 operation_n(left, right, context, config, |left, right| {
1085 let mut union = Vec::with_capacity(left.values.len() + right.values.len());
1086 union.extend(left.values.iter());
1087 union.extend(right.values.iter());
1088
1089 Ok(left.new_context_from(union))
1090 })
1091 .await
1092}
1093
1094async fn evaluate_type_operation<'a>(
1095 expression: &Expression,
1096 type_name: &QualifiedIdentifier,
1097 context: Context<'a>,
1098 config: Option<Arc<Config<'a>>>,
1099 return_context: bool,
1100) -> Result<Context<'a>, FHIRPathError> {
1101 let left = evaluate_expression(expression, context, config).await?;
1102
1103 if left.values.len() > 1 {
1104 return Err(FHIRPathError::OperationError(
1105 OperationError::InvalidCardinality,
1106 ));
1107 }
1108
1109 let Some(type_name) = type_name.0.first().map(|id| &id.0) else {
1110 return Ok(left.new_context_from(vec![]));
1111 };
1112
1113 let filtered = filter_by_type(type_name, &left);
1114
1115 if return_context {
1116 Ok(filtered)
1117 } else {
1118 Ok(left.new_context_from(vec![
1119 left.allocate_literal(FHIRBoolean {
1120 value: Some(!filtered.values.is_empty()),
1121 ..Default::default()
1122 })
1123 .await,
1124 ]))
1125 }
1126}
1127
1128async fn evaluate_is<'a>(
1129 expression: &Expression,
1130 type_name: &QualifiedIdentifier,
1131 context: Context<'a>,
1132 config: Option<Arc<Config<'a>>>,
1133) -> Result<Context<'a>, FHIRPathError> {
1134 evaluate_type_operation(expression, type_name, context, config, false).await
1135}
1136
1137async fn evaluate_as<'a>(
1138 expression: &Expression,
1139 type_name: &QualifiedIdentifier,
1140 context: Context<'a>,
1141 config: Option<Arc<Config<'a>>>,
1142) -> Result<Context<'a>, FHIRPathError> {
1143 evaluate_type_operation(expression, type_name, context, config, true).await
1144}
1145
1146fn evaluate_expression<'a>(
1147 ast: &Expression,
1148 context: Context<'a>,
1149 config: Option<Arc<Config<'a>>>,
1150) -> Pin<Box<impl Future<Output = Result<Context<'a>, FHIRPathError>>>> {
1151 Box::pin(async move {
1152 match ast {
1153 Expression::Operation(operation) => {
1154 evaluate_operation(operation, context, config).await
1155 }
1156 Expression::Singular(singular_ast) => {
1157 evaluate_singular(singular_ast, context, config).await
1158 }
1159 }
1160 })
1161}
1162
1163#[derive(Debug)]
1164pub enum ResolvedValue {
1165 Box(Box<dyn MetaValue>),
1166 Arc(Arc<dyn MetaValue>),
1167}
1168
1169impl ResolvedValue {
1170 #[must_use]
1171 pub fn as_meta_value(&self) -> &dyn MetaValue {
1172 match self {
1173 ResolvedValue::Box(b) => &**b,
1174 ResolvedValue::Arc(a) => &**a,
1175 }
1176 }
1177}
1178
1179pub struct Context<'a> {
1180 allocator: Arc<Mutex<allocators::bumpalo::Allocator>>,
1181 values: Vec<&'a dyn MetaValue>,
1182}
1183
1184pub type ResolvedValueFuture = Pin<Box<dyn Future<Output = Option<ResolvedValue>> + Send>>;
1185pub type ResolveValueCallback = Box<dyn Fn(String) -> ResolvedValueFuture + Send + Sync>;
1186
1187pub enum ExternalConstantResolver<'a> {
1188 Function(ResolveValueCallback),
1189 Variable(Arc<HashMap<String, &'a dyn MetaValue>>),
1190}
1191
1192#[derive(Default)]
1193pub struct Config<'a> {
1194 resource_id: Option<String>,
1196 variable_resolver: Option<ExternalConstantResolver<'a>>,
1197}
1198
1199impl<'a> Config<'a> {
1200 #[must_use]
1201 pub fn builder() -> Self {
1202 Config::default()
1203 }
1204
1205 #[must_use]
1206 pub fn with_variable_resolver(mut self, resolver: ExternalConstantResolver<'a>) -> Self {
1207 self.variable_resolver = Some(resolver);
1208 self
1209 }
1210
1211 #[must_use]
1212 pub fn with_resource_id(mut self, resource_id: String) -> Self {
1213 self.resource_id = Some(resource_id);
1214 self
1215 }
1216}
1217
1218async fn resolve_external_constant<'a>(
1219 name: &str,
1220 resolver: Option<&ExternalConstantResolver<'a>>,
1221 context: Context<'a>,
1222) -> Result<Context<'a>, FHIRPathError> {
1223 let external_constant = match resolver {
1224 Some(ExternalConstantResolver::Function(func)) => {
1225 let result = func(name.to_string()).await;
1226
1227 if let Some(result) = result {
1228 Some(context.allocate(result).await)
1229 } else {
1230 None
1231 }
1232 }
1233 Some(ExternalConstantResolver::Variable(map)) => map.get(name).copied(),
1234 None => None,
1235 };
1236
1237 if let Some(result) = external_constant {
1238 return Ok(context.new_context_from(vec![result]));
1239 }
1240 Ok(context.new_context_from(vec![]))
1241}
1242
1243impl<'a> IntoIterator for &'a Context<'_> {
1244 type Item = &'a dyn haste_reflect::MetaValue;
1245
1246 type IntoIter = std::boxed::Box<
1247 dyn std::iter::Iterator<Item = &'a (dyn haste_reflect::MetaValue + 'static)> + 'a,
1248 >;
1249
1250 fn into_iter(self) -> Self::IntoIter {
1251 self.iter()
1252 }
1253}
1254
1255impl<'a> Context<'a> {
1256 fn new(
1257 values: Vec<&'a dyn MetaValue>,
1258 allocator: Arc<Mutex<allocators::bumpalo::Allocator>>,
1259 ) -> Self {
1260 Self { allocator, values }
1261 }
1262 fn new_context_from(&self, values: Vec<&'a dyn MetaValue>) -> Self {
1263 Self {
1264 allocator: self.allocator.clone(),
1265 values,
1266 }
1267 }
1268 async fn allocate(&self, value: ResolvedValue) -> &'a dyn MetaValue {
1269 self.allocator.lock().await.allocate_resolved(value)
1270 }
1271
1272 async fn allocate_literal<T: MetaValue>(&self, value: T) -> &'a dyn MetaValue {
1273 self.allocator.lock().await.allocate_literal(value)
1274 }
1275 #[must_use]
1276 pub fn iter(&'a self) -> Box<dyn Iterator<Item = &'a dyn MetaValue> + 'a> {
1277 Box::new(self.values.iter().copied())
1278 }
1279}
1280
1281impl Clone for Context<'_> {
1282 fn clone(&self) -> Self {
1283 Self {
1284 allocator: self.allocator.clone(),
1285 values: self.values.clone(),
1286 }
1287 }
1288}
1289
1290pub struct FPEngine {}
1291
1292static AST: LazyLock<Arc<DashMap<String, Expression>>> = LazyLock::new(|| Arc::new(DashMap::new()));
1293
1294fn get_ast(
1295 path: &str,
1296) -> Result<dashmap::mapref::one::Ref<'static, String, Expression>, FHIRPathError> {
1297 let ast: dashmap::mapref::one::Ref<'_, String, Expression> =
1298 if let Some(expression_ast) = AST.get(path) {
1299 expression_ast
1300 } else {
1301 AST.insert(path.to_string(), parser::parse(path)?);
1302 AST.get(path).ok_or_else(|| {
1303 FHIRPathError::InternalError("Failed to find path post insert".to_string())
1304 })?
1305 };
1306
1307 Ok(ast)
1308}
1309
1310impl Default for FPEngine {
1311 fn default() -> Self {
1312 Self::new()
1313 }
1314}
1315
1316impl FPEngine {
1317 #[must_use]
1318 pub fn new() -> Self {
1319 Self {}
1320 }
1321
1322 pub async fn evaluate<'a, 'b>(
1332 &self,
1333 path: &str,
1334 values: Vec<&'a dyn MetaValue>,
1335 ) -> Result<Context<'b>, FHIRPathError>
1336 where
1337 'a: 'b,
1338 {
1339 let ast = get_ast(path)?;
1340
1341 let allocator: Arc<Mutex<allocators::bumpalo::Allocator>> =
1343 Arc::new(Mutex::new(allocators::bumpalo::Allocator::new()));
1344
1345 let context = Context::new(values, allocator.clone());
1346
1347 let result = evaluate_expression(&ast, context, None).await?;
1348 Ok(result)
1349 }
1350
1351 pub async fn evaluate_with_config<'a, 'b>(
1361 &self,
1362 path: &str,
1363 values: Vec<&'a dyn MetaValue>,
1364 config: Arc<Config<'b>>,
1365 ) -> Result<Context<'b>, FHIRPathError>
1366 where
1367 'a: 'b,
1368 {
1369 let ast = get_ast(path)?;
1370
1371 let allocator = Arc::new(Mutex::new(allocators::bumpalo::Allocator::new()));
1373
1374 let context = Context::new(values, allocator.clone());
1375
1376 let result = evaluate_expression(&ast, context, Some(config)).await?;
1377
1378 Ok(result)
1379 }
1380}
1381
1382#[cfg(test)]
1383mod tests {
1384 use super::*;
1385 use haste_fhir_model::r4::{
1386 datetime::DateTime,
1387 generated::{
1388 resources::{
1389 Bundle, Group, GroupMember, Patient, PatientDeceasedTypeChoice, PatientLink,
1390 Resource, SearchParameter,
1391 },
1392 types::{
1393 Extension, ExtensionValueTypeChoice, FHIRDateTime, FHIRString, FHIRUri, HumanName,
1394 Identifier, Reference,
1395 },
1396 },
1397 };
1398
1399 use haste_reflect_derive::Reflect;
1400
1401 #[derive(Reflect, Debug)]
1402 #[fhir_type = "BackboneElement"]
1403 struct C {
1404 c: String,
1405 }
1406
1407 #[derive(Reflect, Debug)]
1408 #[fhir_type = "BackboneElement"]
1409 struct B {
1410 b: Vec<Box<C>>,
1411 }
1412
1413 #[derive(Reflect, Debug)]
1414 #[fhir_type = "BackboneElement"]
1415 struct A {
1416 a: Vec<Box<B>>,
1417 }
1418
1419 fn load_search_parameters() -> Vec<SearchParameter> {
1420 let json =
1421 include_str!("../../artifacts/artifacts/r4/hl7/minified/search-parameters.min.json");
1422 let bundle = serde_json::from_str::<Bundle>(json).unwrap();
1423
1424 let search_parameters: Vec<SearchParameter> = bundle
1425 .entry
1426 .unwrap_or_else(|| Vec::new())
1427 .into_iter()
1428 .map(|e| e.resource)
1429 .filter(|e| e.is_some())
1430 .filter_map(|e| match e {
1431 Some(k) => match *k {
1432 Resource::SearchParameter(sp) => Some(sp),
1433 _ => None,
1434 },
1435 _ => None,
1436 })
1437 .collect();
1438
1439 search_parameters
1440 }
1441
1442 #[tokio::test]
1443 async fn filter_typechoice_test() {
1444 let patient = Patient {
1445 id: Some("patient-id".to_string()),
1446 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1447 value: Some(true),
1448 ..Default::default()
1449 }))),
1450 ..Default::default()
1451 };
1452
1453 let engine = FPEngine::new();
1454 let result = engine
1455 .evaluate("(Patient.deceased.ofType(dateTime))", vec![&patient])
1456 .await
1457 .unwrap();
1458
1459 assert_eq!(result.values.len(), 0);
1460
1461 let result = engine
1462 .evaluate("(Patient.deceased.ofType(boolean))", vec![&patient])
1463 .await
1464 .unwrap();
1465
1466 let value = result.values[0];
1467 let boolean_value: &FHIRBoolean = value
1468 .as_any()
1469 .downcast_ref::<FHIRBoolean>()
1470 .expect("Failed to downcast to FHIRBoolean");
1471
1472 assert_eq!(boolean_value.value, Some(true));
1473
1474 let patient = Patient {
1475 id: Some("patient-id".to_string()),
1476 deceased: Some(PatientDeceasedTypeChoice::DateTime(Box::new(
1477 FHIRDateTime {
1478 value: Some(DateTime::Year(1980)),
1479 ..Default::default()
1480 },
1481 ))),
1482 ..Default::default()
1483 };
1484
1485 let result = engine
1486 .evaluate("(Patient.deceased.ofType(boolean))", vec![&patient])
1487 .await
1488 .unwrap();
1489
1490 assert_eq!(result.values.len(), 0);
1491
1492 let result = engine
1493 .evaluate("(Patient.deceased.ofType(dateTime))", vec![&patient])
1494 .await
1495 .unwrap();
1496
1497 assert_eq!(result.values.len(), 1);
1498
1499 let value = result.values[0];
1500 let datetime_value: &FHIRDateTime = value
1501 .as_any()
1502 .downcast_ref::<FHIRDateTime>()
1503 .expect("Failed to downcast to FHIRDateTime");
1504
1505 assert_eq!(datetime_value.value, Some(DateTime::Year(1980)));
1506 }
1507
1508 #[tokio::test]
1509 async fn test_variable_resolution() {
1510 let engine = FPEngine::new();
1511 let patient = Patient {
1512 id: Some("my-patient".to_string()),
1513 ..Default::default()
1514 };
1515 let config = Arc::new(
1516 Config::builder().with_variable_resolver(ExternalConstantResolver::Variable(Arc::new(
1517 vec![("patient".to_string(), &patient as &dyn MetaValue)]
1518 .into_iter()
1519 .collect(),
1520 ))),
1521 );
1522
1523 let result = engine
1524 .evaluate_with_config("%patient", vec![], config.clone())
1525 .await
1526 .unwrap();
1527
1528 assert_eq!(result.values.len(), 1);
1529 let p = result.values[0].as_any().downcast_ref::<Patient>().unwrap();
1530
1531 assert_eq!(p.id, patient.id);
1532
1533 let result_failed = engine
1534 .evaluate_with_config("%nobody", vec![], config)
1535 .await
1536 .unwrap();
1537
1538 assert_eq!(result_failed.values.len(), 0);
1539 }
1540
1541 #[tokio::test]
1542 async fn test_where_clause() {
1543 let engine = FPEngine::new();
1544 let mut patient = Patient::default();
1545 let mut identifier = Identifier::default();
1546 let extension = Extension {
1547 id: None,
1548 url: "test-extension".to_string(),
1549 extension: None,
1550 value: Some(ExtensionValueTypeChoice::String(Box::new(FHIRString {
1551 id: None,
1552 extension: None,
1553 value: Some("example value".to_string()),
1554 }))),
1555 };
1556 identifier.value = Some(Box::new(FHIRString {
1557 id: None,
1558 extension: Some(vec![extension]),
1559 value: Some("12345".to_string()),
1560 }));
1561 patient.identifier_ = Some(vec![identifier]);
1562
1563 let context = engine
1564 .evaluate(
1565 "$this.identifier.value.where($this.extension.value.exists())",
1566 vec![&patient],
1567 )
1568 .await;
1569
1570 assert_eq!(context.unwrap().values.len(), 1);
1571
1572 let context = engine
1573 .evaluate(
1574 "$this.identifier.value.where($this.extension.extension.exists())",
1575 vec![&patient],
1576 )
1577 .await;
1578 assert_eq!(context.unwrap().values.len(), 0);
1579 }
1580
1581 #[tokio::test]
1582 async fn test_all_parameters() {
1583 let search_parameters = load_search_parameters();
1584 for param in search_parameters.iter() {
1585 if let Some(expression) = ¶m.expression {
1586 let engine = FPEngine::new();
1587 let context = engine
1588 .evaluate(expression.value.as_ref().unwrap().as_str(), vec![])
1589 .await;
1590
1591 if let Err(err) = context {
1592 panic!(
1593 "Failed to evaluate search parameter '{}': {}",
1594 expression.value.as_ref().unwrap(),
1595 err
1596 );
1597 }
1598 }
1599 }
1600 }
1601
1602 fn test_patient() -> Patient {
1603 let mut patient = Patient::default();
1604 let mut name = HumanName::default();
1605 name.given = Some(vec![FHIRString {
1606 id: None,
1607 extension: None,
1608 value: Some("Bob".to_string()),
1609 }]);
1610
1611 let mut mrn_identifier = Identifier::default();
1612 mrn_identifier.value = Some(Box::new(FHIRString {
1613 id: None,
1614 extension: None,
1615 value: Some("mrn-12345".to_string()),
1616 }));
1617 mrn_identifier.system = Some(Box::new(FHIRUri {
1618 id: None,
1619 extension: None,
1620 value: Some("mrn".to_string()),
1621 }));
1622
1623 let mut ssn_identifier = Identifier::default();
1624 ssn_identifier.value = Some(Box::new(FHIRString {
1625 id: None,
1626 extension: None,
1627 value: Some("ssn-12345".to_string()),
1628 }));
1629 ssn_identifier.system = Some(Box::new(FHIRUri {
1630 id: None,
1631 extension: None,
1632 value: Some("ssn".to_string()),
1633 }));
1634
1635 mrn_identifier.system = Some(Box::new(FHIRUri {
1636 id: None,
1637 extension: None,
1638 value: Some("mrn".to_string()),
1639 }));
1640
1641 patient.identifier_ = Some(vec![mrn_identifier, ssn_identifier]);
1642 patient.name = Some(vec![name]);
1643 patient
1644 }
1645
1646 #[tokio::test]
1647 async fn indexing_tests() {
1648 let engine = FPEngine::new();
1649 let patient = test_patient();
1650
1651 let given_name = engine
1652 .evaluate("$this.name.given[0]", vec![&patient])
1653 .await
1654 .unwrap();
1655
1656 assert_eq!(given_name.values.len(), 1);
1657 let value = given_name.values[0];
1658 let name: &FHIRString = value
1659 .as_any()
1660 .downcast_ref::<FHIRString>()
1661 .expect("Failed to downcast to FHIRString");
1662
1663 assert_eq!(name.value.as_deref(), Some("Bob"));
1664
1665 let ssn_identifier = engine
1666 .evaluate("$this.identifier[1]", vec![&patient])
1667 .await
1668 .unwrap();
1669
1670 assert_eq!(ssn_identifier.values.len(), 1);
1671 let value = ssn_identifier.values[0];
1672 let identifier: &Identifier = value
1673 .as_any()
1674 .downcast_ref::<Identifier>()
1675 .expect("Failed to downcast to Identifier");
1676
1677 assert_eq!(
1678 identifier.value.as_ref().unwrap().value.as_deref(),
1679 Some("ssn-12345")
1680 );
1681
1682 let all_identifiers = engine
1683 .evaluate("$this.identifier", vec![&patient])
1684 .await
1685 .unwrap();
1686 assert_eq!(all_identifiers.values.len(), 2);
1687 }
1688
1689 #[tokio::test]
1690 async fn where_testing() {
1691 let engine = FPEngine::new();
1692 let patient = test_patient();
1693
1694 let name_where_clause = engine
1695 .evaluate(
1696 "$this.name.given.where($this.value = 'Bob')",
1697 vec![&patient],
1698 )
1699 .await
1700 .unwrap();
1701
1702 assert_eq!(name_where_clause.values.len(), 1);
1703 let value = name_where_clause.values[0];
1704 let name: &FHIRString = value
1705 .as_any()
1706 .downcast_ref::<FHIRString>()
1707 .expect("Failed to downcast to FHIRString");
1708
1709 assert_eq!(name.value.as_deref(), Some("Bob"));
1710
1711 let ssn_identifier_clause = engine
1712 .evaluate(
1713 "$this.identifier.where($this.system.value = 'ssn')",
1714 vec![&patient],
1715 )
1716 .await
1717 .unwrap();
1718 assert_eq!(ssn_identifier_clause.values.len(), 1);
1719
1720 let ssn_identifier = ssn_identifier_clause.values[0]
1721 .as_any()
1722 .downcast_ref::<Identifier>()
1723 .expect("Failed to downcast to Identifier");
1724
1725 assert_eq!(
1726 ssn_identifier.value.as_ref().unwrap().value.as_deref(),
1727 Some("ssn-12345")
1728 );
1729 }
1730
1731 #[tokio::test]
1732 async fn test_equality() {
1733 let engine = FPEngine::new();
1734
1735 let string_equal = engine.evaluate("'test' = 'test'", vec![]).await.unwrap();
1737 for r in string_equal.iter() {
1738 let b: bool = r
1739 .as_any()
1740 .downcast_ref::<FHIRBoolean>()
1741 .unwrap()
1742 .value
1743 .unwrap()
1744 .clone();
1745 assert_eq!(b, true);
1746 }
1747 let string_unequal = engine.evaluate("'invalid' = 'test'", vec![]).await.unwrap();
1748 for r in string_unequal.iter() {
1749 let b: bool = r
1750 .as_any()
1751 .downcast_ref::<FHIRBoolean>()
1752 .unwrap()
1753 .value
1754 .unwrap()
1755 .clone();
1756 assert_eq!(b, false);
1757 }
1758
1759 let number_equal = engine.evaluate("12 = 12", vec![]).await.unwrap();
1761 for r in number_equal.iter() {
1762 let b: bool = r
1763 .as_any()
1764 .downcast_ref::<FHIRBoolean>()
1765 .unwrap()
1766 .value
1767 .unwrap()
1768 .clone();
1769 assert_eq!(b, true);
1770 }
1771 let number_unequal = engine.evaluate("13 = 12", vec![]).await.unwrap();
1772 for r in number_unequal.iter() {
1773 let b: bool = r
1774 .as_any()
1775 .downcast_ref::<FHIRBoolean>()
1776 .unwrap()
1777 .value
1778 .unwrap()
1779 .clone();
1780 assert_eq!(b, false);
1781 }
1782
1783 let bool_equal = engine.evaluate("false = false", vec![]).await.unwrap();
1785 for r in bool_equal.iter() {
1786 let b: bool = r
1787 .as_any()
1788 .downcast_ref::<FHIRBoolean>()
1789 .unwrap()
1790 .value
1791 .unwrap()
1792 .clone();
1793 assert_eq!(b, true);
1794 }
1795 let bool_unequal = engine.evaluate("false = true", vec![]).await.unwrap();
1796 for r in bool_unequal.iter() {
1797 let b: bool = r
1798 .as_any()
1799 .downcast_ref::<FHIRBoolean>()
1800 .unwrap()
1801 .value
1802 .unwrap()
1803 .clone();
1804 assert_eq!(b, false);
1805 }
1806
1807 let bool_equal = engine.evaluate("12 = 13 = false", vec![]).await.unwrap();
1809 for r in bool_equal.iter() {
1810 let b: bool = r
1811 .as_any()
1812 .downcast_ref::<FHIRBoolean>()
1813 .unwrap()
1814 .value
1815 .unwrap()
1816 .clone();
1817 assert_eq!(b, true);
1818 }
1819 let bool_unequal = engine.evaluate("12 = 13 = true", vec![]).await.unwrap();
1820 for r in bool_unequal.iter() {
1821 let b: bool = r
1822 .as_any()
1823 .downcast_ref::<FHIRBoolean>()
1824 .unwrap()
1825 .value
1826 .unwrap()
1827 .clone();
1828 assert_eq!(b, false);
1829 }
1830 let bool_unequal = engine.evaluate("12 = (13 - 1)", vec![]).await.unwrap();
1831 for r in bool_unequal.iter() {
1832 let b: bool = r
1833 .as_any()
1834 .downcast_ref::<FHIRBoolean>()
1835 .unwrap()
1836 .value
1837 .unwrap()
1838 .clone();
1839
1840 assert_eq!(b, true);
1841 }
1842 }
1843
1844 #[tokio::test]
1845 async fn test_string_concat() {
1846 let engine = FPEngine::new();
1847 let patient = test_patient();
1848
1849 let simple_result = engine.evaluate("'Hello' + ' World'", vec![]).await.unwrap();
1850 for r in simple_result.iter() {
1851 let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1852 assert_eq!(s.value, Some("Hello World".to_string()));
1853 }
1854
1855 let simple_result = engine
1856 .evaluate("$this.name.given + ' Miller'", vec![&patient])
1857 .await
1858 .unwrap();
1859 for r in simple_result.iter() {
1860 let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1861 assert_eq!(s.value, Some("Bob Miller".to_string()));
1862 }
1863 }
1864
1865 #[tokio::test]
1866 async fn test_simple() {
1867 let root = A {
1868 a: vec![Box::new(B {
1869 b: vec![Box::new(C {
1870 c: "whatever".to_string(),
1871 })],
1872 })],
1873 };
1874
1875 let engine = FPEngine::new();
1876 let result = engine.evaluate("a.b.c", vec![&root]).await.unwrap();
1877
1878 let strings: Vec<&String> = result
1879 .iter()
1880 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1881 .collect();
1882
1883 assert_eq!(strings, vec!["whatever"]);
1884 }
1885
1886 #[tokio::test]
1887 async fn allocation() {
1888 let engine = FPEngine::new();
1889 let result = engine.evaluate("'asdf'", vec![]).await.unwrap();
1890
1891 for r in result.iter() {
1892 let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1893
1894 assert_eq!(s.value, Some("asdf".to_string()));
1895 }
1896 }
1897
1898 #[tokio::test]
1899 async fn order_operation() {
1900 let engine = FPEngine::new();
1901 let result = engine.evaluate("45 + 2 * 3", vec![]).await.unwrap();
1902
1903 for r in result.iter() {
1904 let s = r.as_any().downcast_ref::<FHIRDecimal>().unwrap().clone();
1905
1906 assert_eq!(s.value, Some(51.0));
1907 }
1908 }
1909
1910 #[tokio::test]
1911 async fn xor_operation() {
1912 let engine = FPEngine::new();
1913 let result = engine.evaluate("true xor true", vec![]).await.unwrap();
1914
1915 for r in result.iter() {
1916 let b: bool = r
1917 .as_any()
1918 .downcast_ref::<FHIRBoolean>()
1919 .unwrap()
1920 .value
1921 .unwrap()
1922 .clone();
1923
1924 assert_eq!(b, false);
1925 }
1926 }
1927
1928 #[tokio::test]
1929 async fn domain_resource_filter() {
1930 let engine = FPEngine::new();
1931
1932 let patient =
1933 serde_json::from_str::<Resource>(r#"{"id": "patient-id", "resourceType": "Patient"}"#)
1934 .unwrap();
1935 let result = engine
1936 .evaluate("Resource.id", vec![&patient])
1937 .await
1938 .unwrap();
1939 let ids: Vec<&String> = result
1940 .iter()
1941 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1942 .collect();
1943
1944 assert_eq!(ids.len(), 1);
1945 assert_eq!(ids[0], "patient-id");
1946
1947 let result2 = engine
1948 .evaluate("DomainResource.id", vec![&patient])
1949 .await
1950 .unwrap();
1951 let ids2: Vec<&String> = result2
1952 .iter()
1953 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1954 .collect();
1955 assert_eq!(ids2.len(), 1);
1956 assert_eq!(ids2[0], "patient-id");
1957 }
1958
1959 #[tokio::test]
1960 async fn type_test() {
1961 let engine = FPEngine::new();
1962 let patient = Patient::default();
1963
1964 let result = engine
1965 .evaluate("$this.type().name", vec![&patient])
1966 .await
1967 .unwrap();
1968 let ids: Vec<&String> = result
1969 .iter()
1970 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1971 .collect();
1972
1973 assert_eq!(ids.len(), 1);
1974 assert_eq!(ids[0], "Patient");
1975 }
1976
1977 #[tokio::test]
1978 async fn resolve_test() {
1979 let engine = FPEngine::new();
1980 let observation = serde_json::from_str::<Resource>(r#"
1981 {
1982 "resourceType": "Observation",
1983 "id": "f001",
1984 "text": {
1985 "status": "generated",
1986 "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Generated Narrative with Details</b></p><p><b>id</b>: f001</p><p><b>identifier</b>: 6323 (OFFICIAL)</p><p><b>status</b>: final</p><p><b>code</b>: Glucose [Moles/volume] in Blood <span>(Details : {LOINC code '15074-8' = 'Glucose [Moles/volume] in Blood', given as 'Glucose [Moles/volume] in Blood'})</span></p><p><b>subject</b>: <a>P. van de Heuvel</a></p><p><b>effective</b>: 02/04/2013 9:30:10 AM --> (ongoing)</p><p><b>issued</b>: 03/04/2013 3:30:10 PM</p><p><b>performer</b>: <a>A. Langeveld</a></p><p><b>value</b>: 6.3 mmol/l<span> (Details: UCUM code mmol/L = 'mmol/L')</span></p><p><b>interpretation</b>: High <span>(Details : {http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation code 'H' = 'High', given as 'High'})</span></p><h3>ReferenceRanges</h3><table><tr><td>-</td><td><b>Low</b></td><td><b>High</b></td></tr><tr><td>*</td><td>3.1 mmol/l<span> (Details: UCUM code mmol/L = 'mmol/L')</span></td><td>6.2 mmol/l<span> (Details: UCUM code mmol/L = 'mmol/L')</span></td></tr></table></div>"
1987 },
1988 "identifier": [
1989 {
1990 "use": "official",
1991 "system": "http://www.bmc.nl/zorgportal/identifiers/observations",
1992 "value": "6323"
1993 }
1994 ],
1995 "status": "final",
1996 "code": {
1997 "coding": [
1998 {
1999 "system": "http://loinc.org",
2000 "code": "15074-8",
2001 "display": "Glucose [Moles/volume] in Blood"
2002 }
2003 ]
2004 },
2005 "subject": {
2006 "reference": "Patient/f001",
2007 "display": "P. van de Heuvel"
2008 },
2009 "effectivePeriod": {
2010 "start": "2013-04-02T09:30:10+01:00"
2011 },
2012 "issued": "2013-04-03T15:30:10+01:00",
2013 "performer": [
2014 {
2015 "reference": "Practitioner/f005",
2016 "display": "A. Langeveld"
2017 }
2018 ],
2019 "valueQuantity": {
2020 "value": 6.3,
2021 "unit": "mmol/l",
2022 "system": "http://unitsofmeasure.org",
2023 "code": "mmol/L"
2024 },
2025 "interpretation": [
2026 {
2027 "coding": [
2028 {
2029 "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
2030 "code": "H",
2031 "display": "High"
2032 }
2033 ]
2034 }
2035 ],
2036 "referenceRange": [
2037 {
2038 "low": {
2039 "value": 3.1,
2040 "unit": "mmol/l",
2041 "system": "http://unitsofmeasure.org",
2042 "code": "mmol/L"
2043 },
2044 "high": {
2045 "value": 6.2,
2046 "unit": "mmol/l",
2047 "system": "http://unitsofmeasure.org",
2048 "code": "mmol/L"
2049 }
2050 }
2051 ]
2052 }
2053 "#).unwrap();
2054
2055 let result = engine
2056 .evaluate(
2057 "Observation.subject.where(resolve() is Patient)",
2058 vec![&observation],
2059 )
2060 .await
2061 .unwrap();
2062
2063 let references: Vec<&Reference> = result
2064 .iter()
2065 .map(|r| r.as_any().downcast_ref::<Reference>().unwrap())
2066 .collect();
2067
2068 assert_eq!(references.len(), 1);
2069 assert_eq!(
2070 references[0].reference.as_ref().unwrap().value,
2071 Some("Patient/f001".to_string())
2072 );
2073 }
2074
2075 #[tokio::test]
2076 async fn children_test() {
2077 let engine = FPEngine::new();
2078 let patient = Patient {
2079 name: Some(vec![HumanName {
2080 given: Some(vec![FHIRString {
2081 value: Some("Alice".to_string()),
2082 ..Default::default()
2083 }]),
2084 ..Default::default()
2085 }]),
2086 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2087 value: Some(true),
2088 ..Default::default()
2089 }))),
2090 ..Default::default()
2091 };
2092
2093 let result = engine
2094 .evaluate("$this.children()", vec![&patient])
2095 .await
2096 .unwrap();
2097
2098 assert_eq!(result.values.len(), 2);
2099 assert_eq!(
2100 result
2101 .values
2102 .iter()
2103 .map(|v| v.fhir_type())
2104 .collect::<Vec<_>>(),
2105 vec!["HumanName", "boolean"]
2106 );
2107 }
2108
2109 #[tokio::test]
2110 async fn repeat_test() {
2111 let engine = FPEngine::new();
2112 let patient = Patient {
2113 name: Some(vec![HumanName {
2114 given: Some(vec![FHIRString {
2115 value: Some("Alice".to_string()),
2116 ..Default::default()
2117 }]),
2118 ..Default::default()
2119 }]),
2120 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2121 value: Some(true),
2122 ..Default::default()
2123 }))),
2124 ..Default::default()
2125 };
2126
2127 let result = engine
2128 .evaluate("$this.name.given", vec![&patient])
2129 .await
2130 .unwrap();
2131
2132 assert_eq!(result.values.len(), 1);
2133
2134 assert_eq!(result.values[0].fhir_type(), "string");
2135
2136 let result = engine
2137 .evaluate("$this.repeat(children())", vec![&patient])
2138 .await
2139 .unwrap();
2140
2141 assert_eq!(
2142 result
2143 .values
2144 .iter()
2145 .map(|v| v.fhir_type())
2146 .collect::<Vec<_>>(),
2147 vec![
2148 "HumanName",
2149 "boolean",
2150 "string",
2151 "http://hl7.org/fhirpath/System.Boolean",
2152 "http://hl7.org/fhirpath/System.String"
2153 ]
2154 );
2155 }
2156 #[tokio::test]
2157 async fn descendants_test() {
2158 let engine = FPEngine::new();
2159 let patient = Patient {
2160 name: Some(vec![HumanName {
2161 given: Some(vec![FHIRString {
2162 value: Some("Alice".to_string()),
2163 ..Default::default()
2164 }]),
2165 ..Default::default()
2166 }]),
2167 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2168 value: Some(true),
2169 ..Default::default()
2170 }))),
2171 ..Default::default()
2172 };
2173 let result = engine
2174 .evaluate("descendants()", vec![&patient])
2175 .await
2176 .unwrap();
2177
2178 assert_eq!(
2179 result
2180 .values
2181 .iter()
2182 .map(|v| v.fhir_type())
2183 .collect::<Vec<_>>(),
2184 vec![
2185 "HumanName",
2186 "boolean",
2187 "string",
2188 "http://hl7.org/fhirpath/System.Boolean",
2189 "http://hl7.org/fhirpath/System.String"
2190 ]
2191 );
2192 }
2193
2194 #[tokio::test]
2195 async fn descendants_test_filter() {
2196 let engine = FPEngine::new();
2197 let patient = Patient {
2198 link: Some(vec![PatientLink {
2199 other: Box::new(Reference {
2200 reference: Some(Box::new(FHIRString {
2201 value: Some("Patient/123".to_string()),
2202 ..Default::default()
2203 })),
2204 ..Default::default()
2205 }),
2206 ..Default::default()
2207 }]),
2208 name: Some(vec![HumanName {
2209 given: Some(vec![FHIRString {
2210 value: Some("Alice".to_string()),
2211 ..Default::default()
2212 }]),
2213 ..Default::default()
2214 }]),
2215 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2216 value: Some(true),
2217 ..Default::default()
2218 }))),
2219 ..Default::default()
2220 };
2221 let result = engine
2222 .evaluate("descendants()", vec![&patient])
2223 .await
2224 .unwrap();
2225
2226 assert_eq!(
2227 result
2228 .values
2229 .iter()
2230 .map(|v| v.fhir_type())
2231 .collect::<Vec<_>>(),
2232 vec![
2233 "HumanName",
2234 "boolean",
2235 "BackboneElement",
2236 "string",
2237 "http://hl7.org/fhirpath/System.Boolean",
2238 "Reference",
2239 "http://hl7.org/fhirpath/System.String",
2240 "string",
2241 "http://hl7.org/fhirpath/System.String"
2242 ]
2243 );
2244
2245 let result = engine
2246 .evaluate("descendants().ofType(Reference)", vec![&patient])
2247 .await
2248 .unwrap();
2249
2250 assert_eq!(
2251 result
2252 .values
2253 .iter()
2254 .map(|v| v.fhir_type())
2255 .collect::<Vec<_>>(),
2256 vec!["Reference",]
2257 );
2258
2259 let value = result.values[0]
2260 .as_any()
2261 .downcast_ref::<Reference>()
2262 .unwrap();
2263
2264 assert_eq!(
2265 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2266 "Patient/123"
2267 );
2268 }
2269
2270 #[tokio::test]
2271 async fn try_unsafe_set_from_ref() {
2272 let engine = FPEngine::new();
2273 let patient = Patient {
2274 link: Some(vec![PatientLink {
2275 other: Box::new(Reference {
2276 reference: Some(Box::new(FHIRString {
2277 value: Some("Patient/123".to_string()),
2278 ..Default::default()
2279 })),
2280 ..Default::default()
2281 }),
2282 ..Default::default()
2283 }]),
2284 name: Some(vec![HumanName {
2285 given: Some(vec![FHIRString {
2286 value: Some("Alice".to_string()),
2287 ..Default::default()
2288 }]),
2289 ..Default::default()
2290 }]),
2291 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2292 value: Some(true),
2293 ..Default::default()
2294 }))),
2295 ..Default::default()
2296 };
2297
2298 let result = engine
2299 .evaluate("descendants().ofType(Reference)", vec![&patient])
2300 .await
2301 .unwrap();
2302
2303 assert_eq!(
2304 result
2305 .values
2306 .iter()
2307 .map(|v| v.fhir_type())
2308 .collect::<Vec<_>>(),
2309 vec!["Reference",]
2310 );
2311
2312 let value = result.values[0]
2313 .as_any()
2314 .downcast_ref::<Reference>()
2315 .unwrap();
2316
2317 assert_eq!(
2318 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2319 "Patient/123"
2320 );
2321
2322 unsafe {
2325 let r = value as *const Reference;
2326 let mut_ptr = r as *mut Reference;
2327
2328 (*mut_ptr).reference = Some(Box::new(FHIRString {
2329 value: Some("Patient/456".to_string()),
2330 ..Default::default()
2331 }));
2332 }
2333
2334 assert_eq!(
2335 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2336 "Patient/456"
2337 );
2338
2339 assert_eq!(
2340 patient.link.as_ref().unwrap()[0]
2341 .other
2342 .reference
2343 .as_ref()
2344 .unwrap()
2345 .value
2346 .as_ref()
2347 .unwrap(),
2348 "Patient/456"
2349 );
2350 }
2351
2352 #[tokio::test]
2353 async fn test_external_constant_function() {
2354 let engine = FPEngine::new();
2355
2356 let config = Arc::new(Config::builder().with_variable_resolver(
2357 ExternalConstantResolver::Function(Box::new(|v| {
2358 Box::pin(async move {
2359 match v.as_ref() {
2360 "test_variable" => Some(ResolvedValue::Box(Box::new(Patient {
2361 name: Some(vec![HumanName {
2362 given: Some(vec![FHIRString {
2363 value: Some("Paul".to_string()),
2364 ..Default::default()
2365 }]),
2366 ..Default::default()
2367 }]),
2368 ..Default::default()
2369 })
2370 as Box<dyn MetaValue>)),
2371 _ => None,
2372 }
2373 })
2374 })),
2375 ));
2376
2377 let result = engine
2378 .evaluate_with_config("%test_variable.name.given", vec![], config)
2379 .await
2380 .unwrap();
2381
2382 let value = result.values[0]
2383 .as_any()
2384 .downcast_ref::<FHIRString>()
2385 .unwrap();
2386
2387 assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
2388 }
2389
2390 #[tokio::test]
2391 async fn test_external_constant_function_reference() {
2392 let engine = FPEngine::new();
2393
2394 let patient = Arc::new(Patient {
2395 name: Some(vec![HumanName {
2396 given: Some(vec![FHIRString {
2397 value: Some("Paul".to_string()),
2398 ..Default::default()
2399 }]),
2400 ..Default::default()
2401 }]),
2402 ..Default::default()
2403 });
2404
2405 let resolver = {
2406 let patient = patient.clone();
2407 ExternalConstantResolver::Function(Box::new(move |v| {
2408 let patient = patient.clone();
2409 Box::pin(async move {
2410 match v.as_ref() {
2412 "test_variable" => Some(ResolvedValue::Arc(patient.clone())),
2413 _ => None,
2414 }
2415 })
2416 }))
2417 };
2418
2419 let config = Arc::new(Config::builder().with_variable_resolver(resolver));
2420
2421 let result = engine
2422 .evaluate_with_config("%test_variable.name.given", vec![], config)
2423 .await
2424 .unwrap();
2425
2426 let value = result.values[0]
2427 .as_any()
2428 .downcast_ref::<FHIRString>()
2429 .unwrap();
2430
2431 assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
2432 }
2433
2434 #[tokio::test]
2435 async fn test_upper_function() {
2436 let engine = FPEngine::new();
2437
2438 let result = engine.evaluate("'hello'.upper()", vec![]).await.unwrap();
2439 assert_eq!(result.values.len(), 1);
2440 let value = result.values[0]
2441 .as_any()
2442 .downcast_ref::<FHIRString>()
2443 .unwrap();
2444 assert_eq!(value.value.as_deref(), Some("HELLO"));
2445
2446 let result = engine.evaluate("'AbCd'.upper()", vec![]).await.unwrap();
2447 let value = result.values[0]
2448 .as_any()
2449 .downcast_ref::<FHIRString>()
2450 .unwrap();
2451 assert_eq!(value.value.as_deref(), Some("ABCD"));
2452
2453 let result = engine.evaluate("'XYZ'.upper()", vec![]).await.unwrap();
2454 let value = result.values[0]
2455 .as_any()
2456 .downcast_ref::<FHIRString>()
2457 .unwrap();
2458 assert_eq!(value.value.as_deref(), Some("XYZ"));
2459 }
2460
2461 #[tokio::test]
2462 async fn test_lower_function() {
2463 let engine = FPEngine::new();
2464
2465 let result = engine.evaluate("'HELLO'.lower()", vec![]).await.unwrap();
2466 assert_eq!(result.values.len(), 1);
2467 let value = result.values[0]
2468 .as_any()
2469 .downcast_ref::<FHIRString>()
2470 .unwrap();
2471 assert_eq!(value.value.as_deref(), Some("hello"));
2472
2473 let result = engine.evaluate("'AbCd'.lower()", vec![]).await.unwrap();
2474 let value = result.values[0]
2475 .as_any()
2476 .downcast_ref::<FHIRString>()
2477 .unwrap();
2478 assert_eq!(value.value.as_deref(), Some("abcd"));
2479
2480 let result = engine.evaluate("'xyz'.lower()", vec![]).await.unwrap();
2481 let value = result.values[0]
2482 .as_any()
2483 .downcast_ref::<FHIRString>()
2484 .unwrap();
2485 assert_eq!(value.value.as_deref(), Some("xyz"));
2486 }
2487
2488 #[tokio::test]
2489 async fn get_resource_key() {
2490 let engine = FPEngine::new();
2491 let fp_config = Config::builder().with_resource_id("asdf".to_string());
2492
2493 let result = engine
2494 .evaluate_with_config("getResourceKey()", vec![], Arc::new(fp_config))
2495 .await
2496 .unwrap();
2497
2498 let k = result.iter().collect::<Vec<_>>();
2499
2500 assert_eq!(k.len(), 1);
2501
2502 let s = k[0].as_any().downcast_ref::<FHIRId>().unwrap();
2503 assert_eq!(s.value.as_deref(), Some("asdf"));
2504 }
2505
2506 #[tokio::test]
2507 async fn get_reference_key() {
2508 let engine = FPEngine::new();
2509
2510 let group = Group {
2511 member: Some(vec![GroupMember {
2512 entity: Box::new(Reference {
2513 reference: Some(Box::new("Patient/123".to_string().into())),
2514 ..Default::default()
2515 }),
2516 ..Default::default()
2517 }]),
2518 ..Default::default()
2519 };
2520
2521 let result = engine
2522 .evaluate("$this.member.entity.getReferenceKey(Patient)", vec![&group])
2523 .await
2524 .expect("Failed to evaluate getReferenceKey");
2525
2526 let ids = result.iter().collect::<Vec<_>>();
2527
2528 assert_eq!(ids.len(), 1);
2529
2530 let s = ids[0].as_any().downcast_ref::<FHIRId>().unwrap();
2531 assert_eq!(s.value.as_deref(), Some("123"));
2532
2533 let result = engine
2534 .evaluate("$this.member.entity.getReferenceKey(Group)", vec![&group])
2535 .await
2536 .expect("Failed to evaluate getReferenceKey");
2537
2538 let ids = result.iter().collect::<Vec<_>>();
2539 assert_eq!(ids.len(), 0);
2540
2541 let result = engine
2542 .evaluate("$this.member.entity.getReferenceKey()", vec![&group])
2543 .await
2544 .expect("Failed to evaluate getReferenceKey");
2545
2546 let ids = result.iter().collect::<Vec<_>>();
2547 assert_eq!(ids.len(), 1);
2548 let s = ids[0].as_any().downcast_ref::<FHIRId>().unwrap();
2549 assert_eq!(s.value.as_deref(), Some("123"));
2550 }
2551
2552 #[tokio::test]
2553 async fn exists_with_clause() {
2554 let patient = Patient {
2555 name: Some(vec![
2556 HumanName {
2557 given: Some(vec![FHIRString {
2558 value: Some("Alice".to_string()),
2559 ..Default::default()
2560 }]),
2561 ..Default::default()
2562 },
2563 HumanName {
2564 given: Some(vec![FHIRString {
2565 value: Some("Matilda".to_string()),
2566 ..Default::default()
2567 }]),
2568 ..Default::default()
2569 },
2570 ]),
2571 ..Default::default()
2572 };
2573
2574 let engine = FPEngine::new();
2575 let result = engine
2576 .evaluate("$this.name.exists(given.exists())", vec![&patient])
2577 .await
2578 .unwrap();
2579
2580 let result = result.iter().collect::<Vec<_>>();
2581
2582 assert_eq!(result.len(), 1);
2583 let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2584 assert_eq!(s.value, Some(true));
2585
2586 let result = engine
2587 .evaluate("$this.name.exists(given.empty())", vec![&patient])
2588 .await
2589 .unwrap();
2590
2591 let result = result.iter().collect::<Vec<_>>();
2592 assert_eq!(result.len(), 1);
2593 let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2594 assert_eq!(s.value, Some(false));
2595
2596 let result = engine
2597 .evaluate("$this.name.exists(given = 'Matilda')", vec![&patient])
2598 .await
2599 .unwrap();
2600
2601 let result = result.iter().collect::<Vec<_>>();
2602 assert_eq!(result.len(), 1);
2603 let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2604 assert_eq!(s.value, Some(true));
2605
2606 let result = engine
2607 .evaluate("$this.name.exists(given = 'Jane')", vec![&patient])
2608 .await
2609 .unwrap();
2610
2611 let result = result.iter().collect::<Vec<_>>();
2612 assert_eq!(result.len(), 1);
2613 let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2614 assert_eq!(s.value, Some(false));
2615 }
2616 #[tokio::test]
2617 async fn test_first() {
2618 let engine = FPEngine::new();
2619
2620 let group = Group {
2621 member: Some(vec![
2622 GroupMember {
2623 entity: Box::new(Reference {
2624 reference: Some(Box::new("Patient/1".to_string().into())),
2625 ..Default::default()
2626 }),
2627 ..Default::default()
2628 },
2629 GroupMember {
2630 entity: Box::new(Reference {
2631 reference: Some(Box::new("Patient/2".to_string().into())),
2632 ..Default::default()
2633 }),
2634 ..Default::default()
2635 },
2636 ]),
2637 ..Default::default()
2638 };
2639
2640 let result = engine
2641 .evaluate("$this.member.entity.first()", vec![&group])
2642 .await
2643 .expect("Failed to evaluate first()");
2644
2645 let references = result.iter().collect::<Vec<_>>();
2646 assert_eq!(references.len(), 1);
2647
2648 let s = references[0].as_any().downcast_ref::<Reference>().unwrap();
2649 assert_eq!(
2650 s.reference.as_ref().unwrap().value,
2651 Some("Patient/1".to_string())
2652 );
2653 }
2654
2655 #[tokio::test]
2656 async fn test_join() {
2657 let engine = FPEngine::new();
2658
2659 let mut patient = test_patient();
2660 patient.name.as_mut().unwrap()[0]
2661 .given
2662 .as_mut()
2663 .unwrap()
2664 .push(FHIRString {
2665 value: Some("David".to_string()),
2666 ..Default::default()
2667 });
2668
2669 let result = engine
2670 .evaluate("$this.name.given.join(',')", vec![&patient])
2671 .await
2672 .expect("Failed to evaluate join()");
2673
2674 let joined_ = result.iter().collect::<Vec<_>>();
2675 assert_eq!(joined_.len(), 1);
2676 let s = joined_[0].as_any().downcast_ref::<FHIRString>().unwrap();
2677 assert_eq!(s.value, Some("Bob,David".to_string()));
2678
2679 let result = engine
2680 .evaluate("$this.name.given.join()", vec![&patient])
2681 .await
2682 .expect("Failed to evaluate join()");
2683
2684 let joined_ = result.iter().collect::<Vec<_>>();
2685 assert_eq!(joined_.len(), 1);
2686 let s = joined_[0].as_any().downcast_ref::<FHIRString>().unwrap();
2687 assert_eq!(s.value, Some("BobDavid".to_string()));
2688
2689 let result = engine.evaluate("$this.name.join()", vec![&patient]).await;
2690
2691 assert_eq!(result.is_err(), true);
2692 }
2693
2694 #[tokio::test]
2695 async fn numerical_comparisons() {
2696 let engine = FPEngine::new();
2697
2698 let result = engine
2699 .evaluate("5 > 5", vec![])
2700 .await
2701 .expect("Failed to evaluate join()");
2702
2703 assert_eq!(result.values.len(), 1);
2704 let b = result.values[0]
2705 .as_any()
2706 .downcast_ref::<FHIRBoolean>()
2707 .unwrap();
2708 assert_eq!(b.value, Some(false));
2709
2710 let result = engine
2711 .evaluate("5 > 4", vec![])
2712 .await
2713 .expect("Failed to evaluate join()");
2714
2715 assert_eq!(result.values.len(), 1);
2716 let b = result.values[0]
2717 .as_any()
2718 .downcast_ref::<FHIRBoolean>()
2719 .unwrap();
2720 assert_eq!(b.value, Some(true));
2721
2722 let result = engine
2723 .evaluate("5 >= 5", vec![])
2724 .await
2725 .expect("Failed to evaluate join()");
2726
2727 assert_eq!(result.values.len(), 1);
2728 let b = result.values[0]
2729 .as_any()
2730 .downcast_ref::<FHIRBoolean>()
2731 .unwrap();
2732 assert_eq!(b.value, Some(true));
2733
2734 let result = engine
2735 .evaluate("4 >= 5", vec![])
2736 .await
2737 .expect("Failed to evaluate join()");
2738
2739 assert_eq!(result.values.len(), 1);
2740 let b = result.values[0]
2741 .as_any()
2742 .downcast_ref::<FHIRBoolean>()
2743 .unwrap();
2744 assert_eq!(b.value, Some(false));
2745
2746 let result = engine
2747 .evaluate("6 <= 5", vec![])
2748 .await
2749 .expect("Failed to evaluate join()");
2750
2751 assert_eq!(result.values.len(), 1);
2752 let b = result.values[0]
2753 .as_any()
2754 .downcast_ref::<FHIRBoolean>()
2755 .unwrap();
2756 assert_eq!(b.value, Some(false));
2757
2758 let result = engine
2759 .evaluate("6 <= 6", vec![])
2760 .await
2761 .expect("Failed to evaluate join()");
2762
2763 assert_eq!(result.values.len(), 1);
2764 let b = result.values[0]
2765 .as_any()
2766 .downcast_ref::<FHIRBoolean>()
2767 .unwrap();
2768 assert_eq!(b.value, Some(true));
2769
2770 let result = engine
2771 .evaluate("6 < 6", vec![])
2772 .await
2773 .expect("Failed to evaluate join()");
2774
2775 assert_eq!(result.values.len(), 1);
2776 let b = result.values[0]
2777 .as_any()
2778 .downcast_ref::<FHIRBoolean>()
2779 .unwrap();
2780 assert_eq!(b.value, Some(false));
2781
2782 let result = engine
2783 .evaluate("6 < 7", vec![])
2784 .await
2785 .expect("Failed to evaluate join()");
2786
2787 assert_eq!(result.values.len(), 1);
2788 let b = result.values[0]
2789 .as_any()
2790 .downcast_ref::<FHIRBoolean>()
2791 .unwrap();
2792 assert_eq!(b.value, Some(true));
2793 }
2794}