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 #[must_use]
1244 pub fn iter(&'a self) -> Box<dyn Iterator<Item = &'a dyn MetaValue> + 'a> {
1245 Box::new(self.values.iter().copied())
1246 }
1247}
1248
1249impl Clone for Context<'_> {
1250 fn clone(&self) -> Self {
1251 Self {
1252 allocator: self.allocator.clone(),
1253 values: self.values.clone(),
1254 }
1255 }
1256}
1257
1258pub struct FPEngine {}
1259
1260static AST: LazyLock<DashMap<String, Arc<Expression>>> = LazyLock::new(DashMap::new);
1261
1262fn get_ast(path: &str) -> Result<Arc<Expression>, FHIRPathError> {
1263 if let Some(expression_ast) = AST.get(path) {
1264 return Ok(expression_ast.clone());
1265 }
1266
1267 let expression_ast = Arc::new(parser::parse(path)?);
1268 AST.insert(path.to_string(), expression_ast.clone());
1269
1270 Ok(expression_ast)
1271}
1272
1273impl Default for FPEngine {
1274 fn default() -> Self {
1275 Self::new()
1276 }
1277}
1278
1279impl FPEngine {
1280 #[must_use]
1281 pub fn new() -> Self {
1282 Self {}
1283 }
1284
1285 pub async fn evaluate<'a, 'b>(
1295 &self,
1296 path: &str,
1297 values: Vec<&'a dyn MetaValue>,
1298 ) -> Result<Context<'b>, FHIRPathError>
1299 where
1300 'a: 'b,
1301 {
1302 let ast = get_ast(path)?;
1303
1304 let allocator: Arc<Mutex<allocators::bumpalo::Allocator>> =
1306 Arc::new(Mutex::new(allocators::bumpalo::Allocator::new()));
1307
1308 let context = Context::new(values, allocator.clone());
1309
1310 let result = evaluate_expression(&ast, context, None).await?;
1311 Ok(result)
1312 }
1313
1314 pub async fn evaluate_with_config<'a, 'b>(
1324 &self,
1325 path: &str,
1326 values: Vec<&'a dyn MetaValue>,
1327 config: Arc<Config<'b>>,
1328 ) -> Result<Context<'b>, FHIRPathError>
1329 where
1330 'a: 'b,
1331 {
1332 let ast = get_ast(path)?;
1333
1334 let allocator = Arc::new(Mutex::new(allocators::bumpalo::Allocator::new()));
1336
1337 let context = Context::new(values, allocator.clone());
1338
1339 let result = evaluate_expression(&ast, context, Some(config)).await?;
1340
1341 Ok(result)
1342 }
1343}
1344
1345#[cfg(test)]
1346mod tests {
1347 use super::*;
1348 use haste_fhir_model::r4::{
1349 datetime::DateTime,
1350 generated::{
1351 resources::{
1352 Bundle, Group, GroupMember, Patient, PatientDeceasedTypeChoice, PatientLink,
1353 Resource, SearchParameter,
1354 },
1355 types::{
1356 Extension, ExtensionValueTypeChoice, FHIRDateTime, FHIRString, FHIRUri, HumanName,
1357 Identifier, Reference,
1358 },
1359 },
1360 };
1361
1362 use haste_reflect_derive::Reflect;
1363
1364 #[derive(Reflect, Debug)]
1365 #[fhir_type = "BackboneElement"]
1366 struct C {
1367 c: String,
1368 }
1369
1370 #[derive(Reflect, Debug)]
1371 #[fhir_type = "BackboneElement"]
1372 struct B {
1373 b: Vec<C>,
1374 }
1375
1376 #[derive(Reflect, Debug)]
1377 #[fhir_type = "BackboneElement"]
1378 struct A {
1379 a: Vec<B>,
1380 }
1381
1382 fn load_search_parameters() -> Vec<SearchParameter> {
1383 let json = include_str!(
1384 "../../../../artifacts/r4/hl7-core/definitions/hl7/search-parameters.min.json"
1385 );
1386 let bundle = serde_json::from_str::<Bundle>(json).unwrap();
1387
1388 bundle
1389 .entry
1390 .unwrap_or_default()
1391 .into_iter()
1392 .filter_map(|e| e.resource)
1393 .filter_map(|e| match *e {
1394 Resource::SearchParameter(sp) => Some(sp),
1395 _ => None,
1396 })
1397 .collect()
1398 }
1399
1400 #[tokio::test]
1401 async fn filter_typechoice_test() {
1402 let patient = Patient {
1403 id: Some("patient-id".to_string()),
1404 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1405 value: Some(true),
1406 ..Default::default()
1407 }))),
1408 ..Default::default()
1409 };
1410
1411 let engine = FPEngine::new();
1412 let result = engine
1413 .evaluate("(Patient.deceased.ofType(dateTime))", vec![&patient])
1414 .await
1415 .unwrap();
1416
1417 assert_eq!(result.values.len(), 0);
1418
1419 let result = engine
1420 .evaluate("(Patient.deceased.ofType(boolean))", vec![&patient])
1421 .await
1422 .unwrap();
1423
1424 let value = result.values[0];
1425 let boolean_value: &FHIRBoolean = value
1426 .as_any()
1427 .downcast_ref::<FHIRBoolean>()
1428 .expect("Failed to downcast to FHIRBoolean");
1429
1430 assert_eq!(boolean_value.value, Some(true));
1431
1432 let patient = Patient {
1433 id: Some("patient-id".to_string()),
1434 deceased: Some(PatientDeceasedTypeChoice::DateTime(Box::new(
1435 FHIRDateTime {
1436 value: Some(DateTime::Year(1980)),
1437 ..Default::default()
1438 },
1439 ))),
1440 ..Default::default()
1441 };
1442
1443 let result = engine
1444 .evaluate("(Patient.deceased.ofType(boolean))", vec![&patient])
1445 .await
1446 .unwrap();
1447
1448 assert_eq!(result.values.len(), 0);
1449
1450 let result = engine
1451 .evaluate("(Patient.deceased.ofType(dateTime))", vec![&patient])
1452 .await
1453 .unwrap();
1454
1455 assert_eq!(result.values.len(), 1);
1456
1457 let value = result.values[0];
1458 let datetime_value: &FHIRDateTime = value
1459 .as_any()
1460 .downcast_ref::<FHIRDateTime>()
1461 .expect("Failed to downcast to FHIRDateTime");
1462
1463 assert_eq!(datetime_value.value, Some(DateTime::Year(1980)));
1464 }
1465
1466 #[tokio::test]
1467 async fn test_variable_resolution() {
1468 let engine = FPEngine::new();
1469 let patient = Patient {
1470 id: Some("my-patient".to_string()),
1471 ..Default::default()
1472 };
1473 let config = Arc::new(
1474 Config::builder().with_variable_resolver(ExternalConstantResolver::Variable(Arc::new(
1475 vec![("patient".to_string(), &patient as &dyn MetaValue)]
1476 .into_iter()
1477 .collect(),
1478 ))),
1479 );
1480
1481 let result = engine
1482 .evaluate_with_config("%patient", vec![], config.clone())
1483 .await
1484 .unwrap();
1485
1486 assert_eq!(result.values.len(), 1);
1487 let p = result.values[0].as_any().downcast_ref::<Patient>().unwrap();
1488
1489 assert_eq!(p.id, patient.id);
1490
1491 let result_failed = engine
1492 .evaluate_with_config("%nobody", vec![], config)
1493 .await
1494 .unwrap();
1495
1496 assert_eq!(result_failed.values.len(), 0);
1497 }
1498
1499 #[tokio::test]
1500 async fn test_where_clause() {
1501 let engine = FPEngine::new();
1502 let mut patient = Patient::default();
1503 let mut identifier = Identifier::default();
1504 let extension = Extension {
1505 id: None,
1506 url: "test-extension".to_string(),
1507 extension: None,
1508 value: Some(ExtensionValueTypeChoice::String(Box::new(FHIRString {
1509 id: None,
1510 extension: None,
1511 value: Some("example value".to_string()),
1512 }))),
1513 };
1514 identifier.value = Some(Box::new(FHIRString {
1515 id: None,
1516 extension: Some(vec![extension]),
1517 value: Some("12345".to_string()),
1518 }));
1519 patient.identifier_ = Some(vec![identifier]);
1520
1521 let context = engine
1522 .evaluate(
1523 "$this.identifier.value.where($this.extension.value.exists())",
1524 vec![&patient],
1525 )
1526 .await;
1527
1528 assert_eq!(context.unwrap().values.len(), 1);
1529
1530 let context = engine
1531 .evaluate(
1532 "$this.identifier.value.where($this.extension.extension.exists())",
1533 vec![&patient],
1534 )
1535 .await;
1536 assert_eq!(context.unwrap().values.len(), 0);
1537 }
1538
1539 #[tokio::test]
1540 async fn test_all_parameters() {
1541 let search_parameters = load_search_parameters();
1542 for param in &search_parameters {
1543 if let Some(expression) = ¶m.expression {
1544 let engine = FPEngine::new();
1545 let context = engine
1546 .evaluate(expression.value.as_ref().unwrap().as_str(), vec![])
1547 .await;
1548
1549 if let Err(err) = context {
1550 panic!(
1551 "Failed to evaluate search parameter '{}': {}",
1552 expression.value.as_ref().unwrap(),
1553 err
1554 );
1555 }
1556 }
1557 }
1558 }
1559
1560 fn test_patient() -> Patient {
1561 let mut patient = Patient::default();
1562 let name = HumanName {
1563 given: Some(vec![FHIRString {
1564 id: None,
1565 extension: None,
1566 value: Some("Bob".to_string()),
1567 }]),
1568 ..Default::default()
1569 };
1570 let mrn_identifier = Identifier {
1571 value: Some(Box::new(FHIRString {
1572 id: None,
1573 extension: None,
1574 value: Some("mrn-12345".to_string()),
1575 })),
1576 system: Some(Box::new(FHIRUri {
1577 id: None,
1578 extension: None,
1579 value: Some("mrn".to_string()),
1580 })),
1581 ..Default::default()
1582 };
1583 let ssn_identifier = Identifier {
1584 value: Some(Box::new(FHIRString {
1585 id: None,
1586 extension: None,
1587 value: Some("ssn-12345".to_string()),
1588 })),
1589 system: Some(Box::new(FHIRUri {
1590 id: None,
1591 extension: None,
1592 value: Some("ssn".to_string()),
1593 })),
1594 ..Default::default()
1595 };
1596
1597 patient.identifier_ = Some(vec![mrn_identifier, ssn_identifier]);
1598 patient.name = Some(vec![name]);
1599 patient
1600 }
1601
1602 #[tokio::test]
1603 async fn indexing_tests() {
1604 let engine = FPEngine::new();
1605 let patient = test_patient();
1606
1607 let given_name = engine
1608 .evaluate("$this.name.given[0]", vec![&patient])
1609 .await
1610 .unwrap();
1611
1612 assert_eq!(given_name.values.len(), 1);
1613 let value = given_name.values[0];
1614 let name: &FHIRString = value
1615 .as_any()
1616 .downcast_ref::<FHIRString>()
1617 .expect("Failed to downcast to FHIRString");
1618
1619 assert_eq!(name.value.as_deref(), Some("Bob"));
1620
1621 let ssn_identifier = engine
1622 .evaluate("$this.identifier[1]", vec![&patient])
1623 .await
1624 .unwrap();
1625
1626 assert_eq!(ssn_identifier.values.len(), 1);
1627 let value = ssn_identifier.values[0];
1628 let identifier: &Identifier = value
1629 .as_any()
1630 .downcast_ref::<Identifier>()
1631 .expect("Failed to downcast to Identifier");
1632
1633 assert_eq!(
1634 identifier.value.as_ref().unwrap().value.as_deref(),
1635 Some("ssn-12345")
1636 );
1637
1638 let all_identifiers = engine
1639 .evaluate("$this.identifier", vec![&patient])
1640 .await
1641 .unwrap();
1642 assert_eq!(all_identifiers.values.len(), 2);
1643 }
1644
1645 #[tokio::test]
1646 async fn where_testing() {
1647 let engine = FPEngine::new();
1648 let patient = test_patient();
1649
1650 let name_where_clause = engine
1651 .evaluate(
1652 "$this.name.given.where($this.value = 'Bob')",
1653 vec![&patient],
1654 )
1655 .await
1656 .unwrap();
1657
1658 assert_eq!(name_where_clause.values.len(), 1);
1659 let value = name_where_clause.values[0];
1660 let name: &FHIRString = value
1661 .as_any()
1662 .downcast_ref::<FHIRString>()
1663 .expect("Failed to downcast to FHIRString");
1664
1665 assert_eq!(name.value.as_deref(), Some("Bob"));
1666
1667 let ssn_identifier_clause = engine
1668 .evaluate(
1669 "$this.identifier.where($this.system.value = 'ssn')",
1670 vec![&patient],
1671 )
1672 .await
1673 .unwrap();
1674 assert_eq!(ssn_identifier_clause.values.len(), 1);
1675
1676 let ssn_identifier = ssn_identifier_clause.values[0]
1677 .as_any()
1678 .downcast_ref::<Identifier>()
1679 .expect("Failed to downcast to Identifier");
1680
1681 assert_eq!(
1682 ssn_identifier.value.as_ref().unwrap().value.as_deref(),
1683 Some("ssn-12345")
1684 );
1685 }
1686
1687 #[tokio::test]
1688 async fn test_equality() {
1689 let engine = FPEngine::new();
1690
1691 let string_equal = engine.evaluate("'test' = 'test'", vec![]).await.unwrap();
1693 for r in &string_equal {
1694 let b = r
1695 .as_any()
1696 .downcast_ref::<FHIRBoolean>()
1697 .unwrap()
1698 .value
1699 .unwrap();
1700 assert!(b);
1701 }
1702 let string_unequal = engine.evaluate("'invalid' = 'test'", vec![]).await.unwrap();
1703 for r in &string_unequal {
1704 let b = r
1705 .as_any()
1706 .downcast_ref::<FHIRBoolean>()
1707 .unwrap()
1708 .value
1709 .unwrap();
1710 assert!(!b);
1711 }
1712
1713 let number_equal = engine.evaluate("12 = 12", vec![]).await.unwrap();
1715 for r in &number_equal {
1716 let b = r
1717 .as_any()
1718 .downcast_ref::<FHIRBoolean>()
1719 .unwrap()
1720 .value
1721 .unwrap();
1722 assert!(b);
1723 }
1724 let number_unequal = engine.evaluate("13 = 12", vec![]).await.unwrap();
1725 for r in &number_unequal {
1726 let b = r
1727 .as_any()
1728 .downcast_ref::<FHIRBoolean>()
1729 .unwrap()
1730 .value
1731 .unwrap();
1732 assert!(!b);
1733 }
1734
1735 let bool_equal = engine.evaluate("false = false", vec![]).await.unwrap();
1737 for r in &bool_equal {
1738 let b = r
1739 .as_any()
1740 .downcast_ref::<FHIRBoolean>()
1741 .unwrap()
1742 .value
1743 .unwrap();
1744 assert!(b);
1745 }
1746 let bool_unequal = engine.evaluate("false = true", vec![]).await.unwrap();
1747 for r in &bool_unequal {
1748 let b = r
1749 .as_any()
1750 .downcast_ref::<FHIRBoolean>()
1751 .unwrap()
1752 .value
1753 .unwrap();
1754 assert!(!b);
1755 }
1756
1757 let bool_equal = engine.evaluate("12 = 13 = false", vec![]).await.unwrap();
1759 for r in &bool_equal {
1760 let b = r
1761 .as_any()
1762 .downcast_ref::<FHIRBoolean>()
1763 .unwrap()
1764 .value
1765 .unwrap();
1766 assert!(b);
1767 }
1768 let bool_unequal = engine.evaluate("12 = 13 = true", vec![]).await.unwrap();
1769 for r in &bool_unequal {
1770 let b = r
1771 .as_any()
1772 .downcast_ref::<FHIRBoolean>()
1773 .unwrap()
1774 .value
1775 .unwrap();
1776 assert!(!b);
1777 }
1778 let bool_unequal = engine.evaluate("12 = (13 - 1)", vec![]).await.unwrap();
1779 for r in &bool_unequal {
1780 let b = r
1781 .as_any()
1782 .downcast_ref::<FHIRBoolean>()
1783 .unwrap()
1784 .value
1785 .unwrap();
1786 assert!(b);
1787 }
1788 }
1789
1790 #[tokio::test]
1791 async fn test_string_concat() {
1792 let engine = FPEngine::new();
1793 let patient = test_patient();
1794
1795 let simple_result = engine.evaluate("'Hello' + ' World'", vec![]).await.unwrap();
1796 for r in &simple_result {
1797 let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1798 assert_eq!(s.value, Some("Hello World".to_string()));
1799 }
1800
1801 let simple_result = engine
1802 .evaluate("$this.name.given + ' Miller'", vec![&patient])
1803 .await
1804 .unwrap();
1805 for r in &simple_result {
1806 let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1807 assert_eq!(s.value, Some("Bob Miller".to_string()));
1808 }
1809 }
1810
1811 #[tokio::test]
1812 async fn test_simple() {
1813 let root = A {
1814 a: vec![B {
1815 b: vec![C {
1816 c: "whatever".to_string(),
1817 }],
1818 }],
1819 };
1820
1821 let engine = FPEngine::new();
1822 let result = engine.evaluate("a.b.c", vec![&root]).await.unwrap();
1823
1824 let strings: Vec<&String> = result
1825 .iter()
1826 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1827 .collect();
1828
1829 assert_eq!(strings, vec!["whatever"]);
1830 }
1831
1832 #[tokio::test]
1833 async fn allocation() {
1834 let engine = FPEngine::new();
1835 let result = engine.evaluate("'asdf'", vec![]).await.unwrap();
1836
1837 for r in &result {
1838 let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1839
1840 assert_eq!(s.value, Some("asdf".to_string()));
1841 }
1842 }
1843
1844 #[tokio::test]
1845 async fn order_operation() {
1846 let engine = FPEngine::new();
1847 let result = engine.evaluate("45 + 2 * 3", vec![]).await.unwrap();
1848
1849 for r in &result {
1850 let s = r.as_any().downcast_ref::<FHIRDecimal>().unwrap().clone();
1851
1852 assert_eq!(s.value, Some(51.0));
1853 }
1854 }
1855
1856 #[tokio::test]
1857 async fn xor_operation() {
1858 let engine = FPEngine::new();
1859 let result = engine.evaluate("true xor true", vec![]).await.unwrap();
1860
1861 for r in &result {
1862 let b = r
1863 .as_any()
1864 .downcast_ref::<FHIRBoolean>()
1865 .unwrap()
1866 .value
1867 .unwrap();
1868
1869 assert!(!b);
1870 }
1871 }
1872
1873 #[tokio::test]
1874 async fn domain_resource_filter() {
1875 let engine = FPEngine::new();
1876
1877 let patient =
1878 serde_json::from_str::<Resource>(r#"{"id": "patient-id", "resourceType": "Patient"}"#)
1879 .unwrap();
1880 let result = engine
1881 .evaluate("Resource.id", vec![&patient])
1882 .await
1883 .unwrap();
1884 let ids: Vec<&String> = result
1885 .iter()
1886 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1887 .collect();
1888
1889 assert_eq!(ids.len(), 1);
1890 assert_eq!(ids[0], "patient-id");
1891
1892 let result2 = engine
1893 .evaluate("DomainResource.id", vec![&patient])
1894 .await
1895 .unwrap();
1896 let ids2: Vec<&String> = result2
1897 .iter()
1898 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1899 .collect();
1900 assert_eq!(ids2.len(), 1);
1901 assert_eq!(ids2[0], "patient-id");
1902 }
1903
1904 #[tokio::test]
1905 async fn type_test() {
1906 let engine = FPEngine::new();
1907 let patient = Patient::default();
1908
1909 let result = engine
1910 .evaluate("$this.type().name", vec![&patient])
1911 .await
1912 .unwrap();
1913 let ids: Vec<&String> = result
1914 .iter()
1915 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1916 .collect();
1917
1918 assert_eq!(ids.len(), 1);
1919 assert_eq!(ids[0], "Patient");
1920 }
1921
1922 #[tokio::test]
1923 async fn resolve_test() {
1924 let engine = FPEngine::new();
1925 let observation = serde_json::from_str::<Resource>(r#"
1926 {
1927 "resourceType": "Observation",
1928 "id": "f001",
1929 "text": {
1930 "status": "generated",
1931 "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>"
1932 },
1933 "identifier": [
1934 {
1935 "use": "official",
1936 "system": "http://www.bmc.nl/zorgportal/identifiers/observations",
1937 "value": "6323"
1938 }
1939 ],
1940 "status": "final",
1941 "code": {
1942 "coding": [
1943 {
1944 "system": "http://loinc.org",
1945 "code": "15074-8",
1946 "display": "Glucose [Moles/volume] in Blood"
1947 }
1948 ]
1949 },
1950 "subject": {
1951 "reference": "Patient/f001",
1952 "display": "P. van de Heuvel"
1953 },
1954 "effectivePeriod": {
1955 "start": "2013-04-02T09:30:10+01:00"
1956 },
1957 "issued": "2013-04-03T15:30:10+01:00",
1958 "performer": [
1959 {
1960 "reference": "Practitioner/f005",
1961 "display": "A. Langeveld"
1962 }
1963 ],
1964 "valueQuantity": {
1965 "value": 6.3,
1966 "unit": "mmol/l",
1967 "system": "http://unitsofmeasure.org",
1968 "code": "mmol/L"
1969 },
1970 "interpretation": [
1971 {
1972 "coding": [
1973 {
1974 "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
1975 "code": "H",
1976 "display": "High"
1977 }
1978 ]
1979 }
1980 ],
1981 "referenceRange": [
1982 {
1983 "low": {
1984 "value": 3.1,
1985 "unit": "mmol/l",
1986 "system": "http://unitsofmeasure.org",
1987 "code": "mmol/L"
1988 },
1989 "high": {
1990 "value": 6.2,
1991 "unit": "mmol/l",
1992 "system": "http://unitsofmeasure.org",
1993 "code": "mmol/L"
1994 }
1995 }
1996 ]
1997 }
1998 "#).unwrap();
1999
2000 let result = engine
2001 .evaluate(
2002 "Observation.subject.where(resolve() is Patient)",
2003 vec![&observation],
2004 )
2005 .await
2006 .unwrap();
2007
2008 let references: Vec<&Reference> = result
2009 .iter()
2010 .map(|r| r.as_any().downcast_ref::<Reference>().unwrap())
2011 .collect();
2012
2013 assert_eq!(references.len(), 1);
2014 assert_eq!(
2015 references[0].reference.as_ref().unwrap().value,
2016 Some("Patient/f001".to_string())
2017 );
2018 }
2019
2020 #[tokio::test]
2021 async fn children_test() {
2022 let engine = FPEngine::new();
2023 let patient = Patient {
2024 name: Some(vec![HumanName {
2025 given: Some(vec![FHIRString {
2026 value: Some("Alice".to_string()),
2027 ..Default::default()
2028 }]),
2029 ..Default::default()
2030 }]),
2031 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2032 value: Some(true),
2033 ..Default::default()
2034 }))),
2035 ..Default::default()
2036 };
2037
2038 let result = engine
2039 .evaluate("$this.children()", vec![&patient])
2040 .await
2041 .unwrap();
2042
2043 assert_eq!(result.values.len(), 2);
2044 assert_eq!(
2045 result
2046 .values
2047 .iter()
2048 .map(|v| v.fhir_type())
2049 .collect::<Vec<_>>(),
2050 vec!["HumanName", "boolean"]
2051 );
2052 }
2053
2054 #[tokio::test]
2055 async fn repeat_test() {
2056 let engine = FPEngine::new();
2057 let patient = Patient {
2058 name: Some(vec![HumanName {
2059 given: Some(vec![FHIRString {
2060 value: Some("Alice".to_string()),
2061 ..Default::default()
2062 }]),
2063 ..Default::default()
2064 }]),
2065 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2066 value: Some(true),
2067 ..Default::default()
2068 }))),
2069 ..Default::default()
2070 };
2071
2072 let result = engine
2073 .evaluate("$this.name.given", vec![&patient])
2074 .await
2075 .unwrap();
2076
2077 assert_eq!(result.values.len(), 1);
2078
2079 assert_eq!(result.values[0].fhir_type(), "string");
2080
2081 let result = engine
2082 .evaluate("$this.repeat(children())", vec![&patient])
2083 .await
2084 .unwrap();
2085
2086 assert_eq!(
2087 result
2088 .values
2089 .iter()
2090 .map(|v| v.fhir_type())
2091 .collect::<Vec<_>>(),
2092 vec![
2093 "HumanName",
2094 "boolean",
2095 "string",
2096 "http://hl7.org/fhirpath/System.Boolean",
2097 "http://hl7.org/fhirpath/System.String"
2098 ]
2099 );
2100 }
2101 #[tokio::test]
2102 async fn descendants_test() {
2103 let engine = FPEngine::new();
2104 let patient = Patient {
2105 name: Some(vec![HumanName {
2106 given: Some(vec![FHIRString {
2107 value: Some("Alice".to_string()),
2108 ..Default::default()
2109 }]),
2110 ..Default::default()
2111 }]),
2112 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2113 value: Some(true),
2114 ..Default::default()
2115 }))),
2116 ..Default::default()
2117 };
2118 let result = engine
2119 .evaluate("descendants()", vec![&patient])
2120 .await
2121 .unwrap();
2122
2123 assert_eq!(
2124 result
2125 .values
2126 .iter()
2127 .map(|v| v.fhir_type())
2128 .collect::<Vec<_>>(),
2129 vec![
2130 "HumanName",
2131 "boolean",
2132 "string",
2133 "http://hl7.org/fhirpath/System.Boolean",
2134 "http://hl7.org/fhirpath/System.String"
2135 ]
2136 );
2137 }
2138
2139 #[tokio::test]
2140 async fn descendants_test_filter() {
2141 let engine = FPEngine::new();
2142 let patient = Patient {
2143 link: Some(vec![PatientLink {
2144 other: Box::new(Reference {
2145 reference: Some(Box::new(FHIRString {
2146 value: Some("Patient/123".to_string()),
2147 ..Default::default()
2148 })),
2149 ..Default::default()
2150 }),
2151 ..Default::default()
2152 }]),
2153 name: Some(vec![HumanName {
2154 given: Some(vec![FHIRString {
2155 value: Some("Alice".to_string()),
2156 ..Default::default()
2157 }]),
2158 ..Default::default()
2159 }]),
2160 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2161 value: Some(true),
2162 ..Default::default()
2163 }))),
2164 ..Default::default()
2165 };
2166 let result = engine
2167 .evaluate("descendants()", vec![&patient])
2168 .await
2169 .unwrap();
2170
2171 assert_eq!(
2172 result
2173 .values
2174 .iter()
2175 .map(|v| v.fhir_type())
2176 .collect::<Vec<_>>(),
2177 vec![
2178 "HumanName",
2179 "boolean",
2180 "BackboneElement",
2181 "string",
2182 "http://hl7.org/fhirpath/System.Boolean",
2183 "Reference",
2184 "http://hl7.org/fhirpath/System.String",
2185 "string",
2186 "http://hl7.org/fhirpath/System.String"
2187 ]
2188 );
2189
2190 let result = engine
2191 .evaluate("descendants().ofType(Reference)", vec![&patient])
2192 .await
2193 .unwrap();
2194
2195 assert_eq!(
2196 result
2197 .values
2198 .iter()
2199 .map(|v| v.fhir_type())
2200 .collect::<Vec<_>>(),
2201 vec!["Reference",]
2202 );
2203
2204 let value = result.values[0]
2205 .as_any()
2206 .downcast_ref::<Reference>()
2207 .unwrap();
2208
2209 assert_eq!(
2210 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2211 "Patient/123"
2212 );
2213 }
2214
2215 #[tokio::test]
2216 async fn try_unsafe_set_from_ref() {
2217 let engine = FPEngine::new();
2218 let patient = Patient {
2219 link: Some(vec![PatientLink {
2220 other: Box::new(Reference {
2221 reference: Some(Box::new(FHIRString {
2222 value: Some("Patient/123".to_string()),
2223 ..Default::default()
2224 })),
2225 ..Default::default()
2226 }),
2227 ..Default::default()
2228 }]),
2229 name: Some(vec![HumanName {
2230 given: Some(vec![FHIRString {
2231 value: Some("Alice".to_string()),
2232 ..Default::default()
2233 }]),
2234 ..Default::default()
2235 }]),
2236 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2237 value: Some(true),
2238 ..Default::default()
2239 }))),
2240 ..Default::default()
2241 };
2242
2243 let result = engine
2244 .evaluate("descendants().ofType(Reference)", vec![&patient])
2245 .await
2246 .unwrap();
2247
2248 assert_eq!(
2249 result
2250 .values
2251 .iter()
2252 .map(|v| v.fhir_type())
2253 .collect::<Vec<_>>(),
2254 vec!["Reference",]
2255 );
2256
2257 let value = result.values[0]
2258 .as_any()
2259 .downcast_ref::<Reference>()
2260 .unwrap();
2261
2262 assert_eq!(
2263 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2264 "Patient/123"
2265 );
2266
2267 unsafe {
2270 let r = std::ptr::from_ref::<Reference>(value);
2271 let mut_ptr = r.cast_mut();
2272
2273 (*mut_ptr).reference = Some(Box::new(FHIRString {
2274 value: Some("Patient/456".to_string()),
2275 ..Default::default()
2276 }));
2277 }
2278
2279 assert_eq!(
2280 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2281 "Patient/456"
2282 );
2283
2284 assert_eq!(
2285 patient.link.as_ref().unwrap()[0]
2286 .other
2287 .reference
2288 .as_ref()
2289 .unwrap()
2290 .value
2291 .as_ref()
2292 .unwrap(),
2293 "Patient/456"
2294 );
2295 }
2296
2297 #[tokio::test]
2298 async fn test_external_constant_function() {
2299 let engine = FPEngine::new();
2300
2301 let config = Arc::new(Config::builder().with_variable_resolver(
2302 ExternalConstantResolver::Function(Box::new(|v| {
2303 Box::pin(async move {
2304 match v.as_ref() {
2305 "test_variable" => Some(ResolvedValue::Box(Box::new(Patient {
2306 name: Some(vec![HumanName {
2307 given: Some(vec![FHIRString {
2308 value: Some("Paul".to_string()),
2309 ..Default::default()
2310 }]),
2311 ..Default::default()
2312 }]),
2313 ..Default::default()
2314 })
2315 as Box<dyn MetaValue>)),
2316 _ => None,
2317 }
2318 })
2319 })),
2320 ));
2321
2322 let result = engine
2323 .evaluate_with_config("%test_variable.name.given", vec![], config)
2324 .await
2325 .unwrap();
2326
2327 let value = result.values[0]
2328 .as_any()
2329 .downcast_ref::<FHIRString>()
2330 .unwrap();
2331
2332 assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
2333 }
2334
2335 #[tokio::test]
2336 async fn test_external_constant_function_reference() {
2337 let engine = FPEngine::new();
2338
2339 let patient = Arc::new(Patient {
2340 name: Some(vec![HumanName {
2341 given: Some(vec![FHIRString {
2342 value: Some("Paul".to_string()),
2343 ..Default::default()
2344 }]),
2345 ..Default::default()
2346 }]),
2347 ..Default::default()
2348 });
2349
2350 let resolver = {
2351 let patient = patient.clone();
2352 ExternalConstantResolver::Function(Box::new(move |v| {
2353 let patient = patient.clone();
2354 Box::pin(async move {
2355 match v.as_ref() {
2357 "test_variable" => Some(ResolvedValue::Arc(patient.clone())),
2358 _ => None,
2359 }
2360 })
2361 }))
2362 };
2363
2364 let config = Arc::new(Config::builder().with_variable_resolver(resolver));
2365
2366 let result = engine
2367 .evaluate_with_config("%test_variable.name.given", vec![], config)
2368 .await
2369 .unwrap();
2370
2371 let value = result.values[0]
2372 .as_any()
2373 .downcast_ref::<FHIRString>()
2374 .unwrap();
2375
2376 assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
2377 }
2378
2379 #[tokio::test]
2380 async fn test_upper_function() {
2381 let engine = FPEngine::new();
2382
2383 let result = engine.evaluate("'hello'.upper()", vec![]).await.unwrap();
2384 assert_eq!(result.values.len(), 1);
2385 let value = result.values[0]
2386 .as_any()
2387 .downcast_ref::<FHIRString>()
2388 .unwrap();
2389 assert_eq!(value.value.as_deref(), Some("HELLO"));
2390
2391 let result = engine.evaluate("'AbCd'.upper()", vec![]).await.unwrap();
2392 let value = result.values[0]
2393 .as_any()
2394 .downcast_ref::<FHIRString>()
2395 .unwrap();
2396 assert_eq!(value.value.as_deref(), Some("ABCD"));
2397
2398 let result = engine.evaluate("'XYZ'.upper()", vec![]).await.unwrap();
2399 let value = result.values[0]
2400 .as_any()
2401 .downcast_ref::<FHIRString>()
2402 .unwrap();
2403 assert_eq!(value.value.as_deref(), Some("XYZ"));
2404 }
2405
2406 #[tokio::test]
2407 async fn test_lower_function() {
2408 let engine = FPEngine::new();
2409
2410 let result = engine.evaluate("'HELLO'.lower()", vec![]).await.unwrap();
2411 assert_eq!(result.values.len(), 1);
2412 let value = result.values[0]
2413 .as_any()
2414 .downcast_ref::<FHIRString>()
2415 .unwrap();
2416 assert_eq!(value.value.as_deref(), Some("hello"));
2417
2418 let result = engine.evaluate("'AbCd'.lower()", vec![]).await.unwrap();
2419 let value = result.values[0]
2420 .as_any()
2421 .downcast_ref::<FHIRString>()
2422 .unwrap();
2423 assert_eq!(value.value.as_deref(), Some("abcd"));
2424
2425 let result = engine.evaluate("'xyz'.lower()", vec![]).await.unwrap();
2426 let value = result.values[0]
2427 .as_any()
2428 .downcast_ref::<FHIRString>()
2429 .unwrap();
2430 assert_eq!(value.value.as_deref(), Some("xyz"));
2431 }
2432
2433 #[tokio::test]
2434 async fn get_resource_key() {
2435 let engine = FPEngine::new();
2436 let fp_config = Config::builder().with_resource_id("asdf".to_string());
2437
2438 let result = engine
2439 .evaluate_with_config("getResourceKey()", vec![], Arc::new(fp_config))
2440 .await
2441 .unwrap();
2442
2443 let k = result.iter().collect::<Vec<_>>();
2444
2445 assert_eq!(k.len(), 1);
2446
2447 let s = k[0].as_any().downcast_ref::<FHIRId>().unwrap();
2448 assert_eq!(s.value.as_deref(), Some("asdf"));
2449 }
2450
2451 #[tokio::test]
2452 async fn get_reference_key() {
2453 let engine = FPEngine::new();
2454
2455 let group = Group {
2456 member: Some(vec![GroupMember {
2457 entity: Box::new(Reference {
2458 reference: Some(Box::new("Patient/123".to_string().into())),
2459 ..Default::default()
2460 }),
2461 ..Default::default()
2462 }]),
2463 ..Default::default()
2464 };
2465
2466 let result = engine
2467 .evaluate("$this.member.entity.getReferenceKey(Patient)", vec![&group])
2468 .await
2469 .expect("Failed to evaluate getReferenceKey");
2470
2471 let ids = result.iter().collect::<Vec<_>>();
2472
2473 assert_eq!(ids.len(), 1);
2474
2475 let s = ids[0].as_any().downcast_ref::<FHIRId>().unwrap();
2476 assert_eq!(s.value.as_deref(), Some("123"));
2477
2478 let result = engine
2479 .evaluate("$this.member.entity.getReferenceKey(Group)", vec![&group])
2480 .await
2481 .expect("Failed to evaluate getReferenceKey");
2482
2483 let ids = result.iter().collect::<Vec<_>>();
2484 assert_eq!(ids.len(), 0);
2485
2486 let result = engine
2487 .evaluate("$this.member.entity.getReferenceKey()", vec![&group])
2488 .await
2489 .expect("Failed to evaluate getReferenceKey");
2490
2491 let ids = result.iter().collect::<Vec<_>>();
2492 assert_eq!(ids.len(), 1);
2493 let s = ids[0].as_any().downcast_ref::<FHIRId>().unwrap();
2494 assert_eq!(s.value.as_deref(), Some("123"));
2495 }
2496
2497 #[tokio::test]
2498 async fn exists_with_clause() {
2499 let patient = Patient {
2500 name: Some(vec![
2501 HumanName {
2502 given: Some(vec![FHIRString {
2503 value: Some("Alice".to_string()),
2504 ..Default::default()
2505 }]),
2506 ..Default::default()
2507 },
2508 HumanName {
2509 given: Some(vec![FHIRString {
2510 value: Some("Matilda".to_string()),
2511 ..Default::default()
2512 }]),
2513 ..Default::default()
2514 },
2515 ]),
2516 ..Default::default()
2517 };
2518
2519 let engine = FPEngine::new();
2520 let result = engine
2521 .evaluate("$this.name.exists(given.exists())", vec![&patient])
2522 .await
2523 .unwrap();
2524
2525 let result = result.iter().collect::<Vec<_>>();
2526
2527 assert_eq!(result.len(), 1);
2528 let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2529 assert_eq!(s.value, Some(true));
2530
2531 let result = engine
2532 .evaluate("$this.name.exists(given.empty())", vec![&patient])
2533 .await
2534 .unwrap();
2535
2536 let result = result.iter().collect::<Vec<_>>();
2537 assert_eq!(result.len(), 1);
2538 let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2539 assert_eq!(s.value, Some(false));
2540
2541 let result = engine
2542 .evaluate("$this.name.exists(given = 'Matilda')", vec![&patient])
2543 .await
2544 .unwrap();
2545
2546 let result = result.iter().collect::<Vec<_>>();
2547 assert_eq!(result.len(), 1);
2548 let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2549 assert_eq!(s.value, Some(true));
2550
2551 let result = engine
2552 .evaluate("$this.name.exists(given = 'Jane')", vec![&patient])
2553 .await
2554 .unwrap();
2555
2556 let result = result.iter().collect::<Vec<_>>();
2557 assert_eq!(result.len(), 1);
2558 let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2559 assert_eq!(s.value, Some(false));
2560 }
2561 #[tokio::test]
2562 async fn test_first() {
2563 let engine = FPEngine::new();
2564
2565 let group = Group {
2566 member: Some(vec![
2567 GroupMember {
2568 entity: Box::new(Reference {
2569 reference: Some(Box::new("Patient/1".to_string().into())),
2570 ..Default::default()
2571 }),
2572 ..Default::default()
2573 },
2574 GroupMember {
2575 entity: Box::new(Reference {
2576 reference: Some(Box::new("Patient/2".to_string().into())),
2577 ..Default::default()
2578 }),
2579 ..Default::default()
2580 },
2581 ]),
2582 ..Default::default()
2583 };
2584
2585 let result = engine
2586 .evaluate("$this.member.entity.first()", vec![&group])
2587 .await
2588 .expect("Failed to evaluate first()");
2589
2590 let references = result.iter().collect::<Vec<_>>();
2591 assert_eq!(references.len(), 1);
2592
2593 let s = references[0].as_any().downcast_ref::<Reference>().unwrap();
2594 assert_eq!(
2595 s.reference.as_ref().unwrap().value,
2596 Some("Patient/1".to_string())
2597 );
2598 }
2599
2600 #[tokio::test]
2601 async fn test_join() {
2602 let engine = FPEngine::new();
2603
2604 let mut patient = test_patient();
2605 patient.name.as_mut().unwrap()[0]
2606 .given
2607 .as_mut()
2608 .unwrap()
2609 .push(FHIRString {
2610 value: Some("David".to_string()),
2611 ..Default::default()
2612 });
2613
2614 let result = engine
2615 .evaluate("$this.name.given.join(',')", vec![&patient])
2616 .await
2617 .expect("Failed to evaluate join()");
2618
2619 let joined_ = result.iter().collect::<Vec<_>>();
2620 assert_eq!(joined_.len(), 1);
2621 let s = joined_[0].as_any().downcast_ref::<FHIRString>().unwrap();
2622 assert_eq!(s.value, Some("Bob,David".to_string()));
2623
2624 let result = engine
2625 .evaluate("$this.name.given.join()", vec![&patient])
2626 .await
2627 .expect("Failed to evaluate join()");
2628
2629 let joined_ = result.iter().collect::<Vec<_>>();
2630 assert_eq!(joined_.len(), 1);
2631 let s = joined_[0].as_any().downcast_ref::<FHIRString>().unwrap();
2632 assert_eq!(s.value, Some("BobDavid".to_string()));
2633
2634 let result = engine.evaluate("$this.name.join()", vec![&patient]).await;
2635
2636 assert!(result.is_err());
2637 }
2638
2639 #[tokio::test]
2640 async fn numerical_comparisons() {
2641 let engine = FPEngine::new();
2642
2643 let result = engine
2644 .evaluate("5 > 5", vec![])
2645 .await
2646 .expect("Failed to evaluate join()");
2647
2648 assert_eq!(result.values.len(), 1);
2649 let b = result.values[0]
2650 .as_any()
2651 .downcast_ref::<FHIRBoolean>()
2652 .unwrap();
2653 assert_eq!(b.value, Some(false));
2654
2655 let result = engine
2656 .evaluate("5 > 4", vec![])
2657 .await
2658 .expect("Failed to evaluate join()");
2659
2660 assert_eq!(result.values.len(), 1);
2661 let b = result.values[0]
2662 .as_any()
2663 .downcast_ref::<FHIRBoolean>()
2664 .unwrap();
2665 assert_eq!(b.value, Some(true));
2666
2667 let result = engine
2668 .evaluate("5 >= 5", vec![])
2669 .await
2670 .expect("Failed to evaluate join()");
2671
2672 assert_eq!(result.values.len(), 1);
2673 let b = result.values[0]
2674 .as_any()
2675 .downcast_ref::<FHIRBoolean>()
2676 .unwrap();
2677 assert_eq!(b.value, Some(true));
2678
2679 let result = engine
2680 .evaluate("4 >= 5", vec![])
2681 .await
2682 .expect("Failed to evaluate join()");
2683
2684 assert_eq!(result.values.len(), 1);
2685 let b = result.values[0]
2686 .as_any()
2687 .downcast_ref::<FHIRBoolean>()
2688 .unwrap();
2689 assert_eq!(b.value, Some(false));
2690
2691 let result = engine
2692 .evaluate("6 <= 5", vec![])
2693 .await
2694 .expect("Failed to evaluate join()");
2695
2696 assert_eq!(result.values.len(), 1);
2697 let b = result.values[0]
2698 .as_any()
2699 .downcast_ref::<FHIRBoolean>()
2700 .unwrap();
2701 assert_eq!(b.value, Some(false));
2702
2703 let result = engine
2704 .evaluate("6 <= 6", vec![])
2705 .await
2706 .expect("Failed to evaluate join()");
2707
2708 assert_eq!(result.values.len(), 1);
2709 let b = result.values[0]
2710 .as_any()
2711 .downcast_ref::<FHIRBoolean>()
2712 .unwrap();
2713 assert_eq!(b.value, Some(true));
2714
2715 let result = engine
2716 .evaluate("6 < 6", vec![])
2717 .await
2718 .expect("Failed to evaluate join()");
2719
2720 assert_eq!(result.values.len(), 1);
2721 let b = result.values[0]
2722 .as_any()
2723 .downcast_ref::<FHIRBoolean>()
2724 .unwrap();
2725 assert_eq!(b.value, Some(false));
2726
2727 let result = engine
2728 .evaluate("6 < 7", vec![])
2729 .await
2730 .expect("Failed to evaluate join()");
2731
2732 assert_eq!(result.values.len(), 1);
2733 let b = result.values[0]
2734 .as_any()
2735 .downcast_ref::<FHIRBoolean>()
2736 .unwrap();
2737 assert_eq!(b.value, Some(true));
2738 }
2739}