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