1mod error;
2mod parser;
3use crate::{
4 error::{FunctionError, OperationError},
5 parser::{Expression, FunctionInvocation, Identifier, Invocation, Literal, Operation, Term},
6};
7use dashmap::DashMap;
8pub use error::FHIRPathError;
9use haste_fhir_model::r4::{
10 conversion::{
11 BOOLEAN_TYPES, NUMBER_TYPES, STRING_TYPES, downcast_bool, downcast_number, downcast_string,
12 },
13 generated::{
14 resources::ResourceType,
15 types::{FHIRBoolean, FHIRDecimal, FHIRInteger, FHIRString, Reference},
16 },
17};
18use haste_reflect::MetaValue;
19use haste_reflect_derive::Reflect;
20use std::pin::Pin;
21use std::{
22 collections::HashMap,
23 sync::{Arc, LazyLock},
24};
25use tokio::sync::Mutex;
26
27mod allocators;
28use allocators::AllocatorTrait;
29
30async fn evaluate_literal<'b>(
31 literal: &Literal,
32 context: Context<'b>,
33) -> Result<Context<'b>, FHIRPathError> {
34 match literal {
35 Literal::String(string) => Ok(context.new_context_from(vec![
36 context
37 .allocate_literal(FHIRString {
38 value: Some(string.clone()),
39 ..Default::default()
40 })
41 .await,
42 ])),
43 Literal::Integer(int) => Ok(context.new_context_from(vec![
44 context
45 .allocate_literal(FHIRInteger {
46 value: Some(int.clone()),
47 ..Default::default()
48 })
49 .await,
50 ])),
51 Literal::Float(decimal) => Ok(context.new_context_from(vec![
52 context
53 .allocate_literal(FHIRDecimal {
54 value: Some(decimal.clone()),
55 ..Default::default()
56 })
57 .await,
58 ])),
59 Literal::Boolean(bool) => Ok(context.new_context_from(vec![
60 context
61 .allocate_literal(FHIRBoolean {
62 value: Some(bool.clone()),
63 ..Default::default()
64 })
65 .await,
66 ])),
67 Literal::Null => Ok(context.new_context_from(vec![])),
68 _ => Err(FHIRPathError::InvalidLiteral(literal.to_owned())),
69 }
70}
71
72async fn evaluate_invocation<'a>(
73 invocation: &Invocation,
74 context: Context<'a>,
75 config: Option<Arc<Config<'a>>>,
76) -> Result<Context<'a>, FHIRPathError> {
77 match invocation {
78 Invocation::This => Ok(context),
79 Invocation::Index(index_expression) => {
80 let index = evaluate_expression(index_expression, context.clone(), config).await?;
81 if index.values.len() != 1 {
82 return Err(FHIRPathError::OperationError(
83 OperationError::InvalidCardinality,
84 ));
85 }
86 let index = downcast_number(index.values[0])? as usize;
87 if let Some(value) = context.values.get(index) {
88 Ok(context.new_context_from(vec![*value]))
89 } else {
90 Ok(context.new_context_from(vec![]))
91 }
92 }
93 Invocation::IndexAccessor => Err(FHIRPathError::NotImplemented("index access".to_string())),
94 Invocation::Total => Err(FHIRPathError::NotImplemented("total".to_string())),
95 Invocation::Identifier(Identifier(id)) => Ok(context.new_context_from(
96 context
97 .values
98 .iter()
99 .flat_map(|v| {
100 v.get_field(id)
101 .map(|v| v.flatten())
102 .unwrap_or_else(|| vec![])
103 })
104 .collect(),
105 )),
106 Invocation::Function(function) => evaluate_function(function, context, config).await,
107 }
108}
109
110async fn evaluate_term<'a>(
111 term: &Term,
112 context: Context<'a>,
113 config: Option<Arc<Config<'a>>>,
114) -> Result<Context<'a>, FHIRPathError> {
115 match term {
116 Term::Literal(literal) => evaluate_literal(literal, context).await,
117 Term::ExternalConstant(constant) => {
118 resolve_external_constant(
119 constant,
120 config.as_ref().and_then(|c| c.variable_resolver.as_ref()),
121 context,
122 )
123 .await
124 }
125 Term::Parenthesized(expression) => evaluate_expression(expression, context, config).await,
126 Term::Invocation(invocation) => evaluate_invocation(invocation, context, config).await,
127 }
128}
129
130async fn evaluate_first_term<'a>(
133 term: &Term,
134 context: Context<'a>,
135 config: Option<Arc<Config<'a>>>,
136) -> Result<Context<'a>, FHIRPathError> {
137 match term {
138 Term::Invocation(invocation) => match invocation {
139 Invocation::Identifier(identifier) => {
140 let type_filter = filter_by_type(&identifier.0, &context);
141 if !type_filter.values.is_empty() {
142 Ok(type_filter)
143 } else {
144 evaluate_invocation(invocation, context, config).await
145 }
146 }
147 _ => evaluate_invocation(invocation, context, config).await,
148 },
149 _ => evaluate_term(term, context, config).await,
150 }
151}
152
153async fn evaluate_singular<'a>(
154 expression: &Vec<Term>,
155 context: Context<'a>,
156 config: Option<Arc<Config<'a>>>,
157) -> Result<Context<'a>, FHIRPathError> {
158 let mut current_context = context;
159
160 let mut term_iterator = expression.iter();
161 let first_term = term_iterator.next();
162 if let Some(first_term) = first_term {
163 current_context = evaluate_first_term(first_term, current_context, config.clone()).await?;
164 }
165
166 for term in term_iterator {
167 current_context = evaluate_term(term, current_context, config.clone()).await?;
168 }
169
170 Ok(current_context)
171}
172
173async fn operation_2<'a>(
174 left: &Expression,
175 right: &Expression,
176 context: Context<'a>,
177 config: Option<Arc<Config<'a>>>,
178 executor: impl Fn(
179 Context<'a>,
180 Context<'a>,
181 )
182 -> Pin<Box<dyn Future<Output = Result<Context<'a>, FHIRPathError>> + Send + 'a>>,
183) -> Result<Context<'a>, FHIRPathError> {
184 let left = evaluate_expression(left, context.clone(), config.clone()).await?;
185 let right = evaluate_expression(right, context, config).await?;
186
187 if left.values.len() == 0 || right.values.len() == 0 {
189 return Ok(left.new_context_from(vec![]));
190 }
191
192 if left.values.len() != 1 || right.values.len() != 1 {
193 return Err(FHIRPathError::OperationError(
194 OperationError::InvalidCardinality,
195 ));
196 }
197
198 executor(left, right).await
199}
200
201async fn operation_n<'a>(
202 left: &Expression,
203 right: &Expression,
204 context: Context<'a>,
205 config: Option<Arc<Config<'a>>>,
206 executor: impl Fn(Context<'a>, Context<'a>) -> Result<Context<'a>, FHIRPathError>,
207) -> Result<Context<'a>, FHIRPathError> {
208 let left = evaluate_expression(left, context.clone(), config.clone()).await?;
209 let right = evaluate_expression(right, context, config).await?;
210 executor(left, right)
211}
212
213enum Cardinality {
214 Zero,
215 One,
216 Many,
217}
218
219fn validate_arguments(
220 ast_arguments: &Vec<Expression>,
221 cardinality: &Cardinality,
222) -> Result<(), FHIRPathError> {
223 match cardinality {
224 Cardinality::Zero => {
225 if ast_arguments.len() != 0 {
226 return Err(FHIRPathError::OperationError(
227 OperationError::InvalidCardinality,
228 ));
229 }
230 }
231 Cardinality::One => {
232 if ast_arguments.len() != 1 {
233 return Err(FHIRPathError::OperationError(
234 OperationError::InvalidCardinality,
235 ));
236 }
237 }
238 Cardinality::Many => {}
239 }
240 Ok(())
241}
242
243fn derive_typename(expression_ast: &Expression) -> Result<String, FHIRPathError> {
244 match expression_ast {
245 Expression::Singular(ast) => match &ast[0] {
246 Term::Invocation(Invocation::Identifier(type_id)) => Ok(type_id.0.clone()),
247 _ => Err(FHIRPathError::FailedTypeNameDerivation),
248 },
249 _ => Err(FHIRPathError::FailedTypeNameDerivation),
250 }
251}
252
253fn check_type_name(type_name: &str, type_to_check: &str) -> bool {
254 match type_to_check {
255 "Resource" | "DomainResource" => ResourceType::try_from(type_name).is_ok(),
256 _ => type_name == type_to_check,
257 }
258}
259
260fn check_type(value: &dyn MetaValue, type_to_check: &str) -> bool {
261 let fhir_type_name = value.fhir_type();
262
263 match fhir_type_name {
264 "Reference" => {
266 if type_to_check == "Reference" {
267 return true;
268 } else if let Some(reference) = value.as_any().downcast_ref::<Reference>() {
269 if let Some(resource_type) = reference
270 .reference
271 .as_ref()
272 .and_then(|r| r.value.as_ref())
273 .and_then(|r| r.split("/").next())
274 {
275 return check_type_name(resource_type, type_to_check);
276 }
277 }
278 false
279 }
280 fhir_type_name => check_type_name(fhir_type_name, type_to_check),
281 }
282}
283
284fn filter_by_type<'a>(type_name: &str, context: &Context<'a>) -> Context<'a> {
285 context.new_context_from(
286 context
287 .values
288 .iter()
289 .filter(|v| check_type(**v, type_name))
290 .map(|v| *v)
291 .collect(),
292 )
293}
294
295#[derive(Debug, Reflect)]
296#[fhir_type = "Element"]
297struct Reflection {
298 name: String,
299}
300
301async fn evaluate_function<'a>(
302 function: &FunctionInvocation,
303 context: Context<'a>,
304 config: Option<Arc<Config<'a>>>,
305) -> Result<Context<'a>, FHIRPathError> {
306 match function.name.0.as_str() {
307 "resolve" => Ok(context),
309 "where" => {
310 validate_arguments(&function.arguments, &Cardinality::One)?;
311
312 let where_condition = &function.arguments[0];
313 let mut new_context = vec![];
314 for value in context.values.iter() {
315 let result = evaluate_expression(
316 where_condition,
317 context.new_context_from(vec![*value]),
318 config.clone(),
319 )
320 .await?;
321
322 if result.values.len() > 1 {
323 return Err(FHIRPathError::InternalError(
324 "Where condition did not return a single value".to_string(),
325 ));
326 } else if !result.values.is_empty() && downcast_bool(result.values[0])? == true {
328 new_context.push(*value);
329 }
330 }
331 Ok(context.new_context_from(new_context))
332 }
333 "ofType" => {
334 validate_arguments(&function.arguments, &Cardinality::One)?;
335
336 let type_name = derive_typename(&function.arguments[0])?;
337 Ok(filter_by_type(&type_name, &context))
338 }
339 "count" => {
340 validate_arguments(&function.arguments, &Cardinality::Zero)?;
341
342 let count = context.values.len() as i64;
343 Ok(context.new_context_from(vec![
344 context
345 .allocate_literal(FHIRInteger {
346 value: Some(count),
347 ..Default::default()
348 })
349 .await,
350 ]))
351 }
352 op @ ("upper" | "lower") => {
353 validate_arguments(&function.arguments, &Cardinality::Zero)?;
354
355 if context.values.is_empty() {
356 return Ok(context.new_context_from(vec![]));
357 }
358 if context.values.len() > 1 {
359 return Err(FunctionError::InvalidCardinality(
360 op.to_string(),
361 context.values.len(),
362 )
363 .into());
364 }
365
366 let input = downcast_string(context.values[0])?;
367 let transformed = match op {
368 "upper" => input.to_uppercase(),
369 "lower" => input.to_lowercase(),
370 _ => unreachable!(),
371 };
372 Ok(context.new_context_from(vec![
373 context
374 .allocate_literal(FHIRString {
375 value: Some(transformed),
376 ..Default::default()
377 })
378 .await,
379 ]))
380 }
381 "as" => {
382 validate_arguments(&function.arguments, &Cardinality::One)?;
383
384 let type_name = derive_typename(&function.arguments[0])?;
385 Ok(filter_by_type(&type_name, &context))
386 }
387 "empty" => {
388 validate_arguments(&function.arguments, &Cardinality::Zero)?;
389 let res = Ok(context.new_context_from(vec![
390 context
391 .allocate_literal(FHIRBoolean {
392 value: Some(context.values.is_empty()),
393 ..Default::default()
394 })
395 .await,
396 ]));
397
398 res
399 }
400 "exists" => {
401 validate_arguments(&function.arguments, &Cardinality::Many)?;
402
403 if function.arguments.len() > 1 {
404 return Err(FunctionError::InvalidCardinality(
405 "exists".to_string(),
406 function.arguments.len(),
407 )
408 .into());
409 }
410
411 let context = if function.arguments.len() == 1 {
412 evaluate_expression(&function.arguments[0], context, config).await?
413 } else {
414 context
415 };
416
417 let res = Ok(context.new_context_from(vec![
418 context
419 .allocate_literal(FHIRBoolean {
420 value: Some(!context.values.is_empty()),
421 ..Default::default()
422 })
423 .await,
424 ]));
425
426 res
427 }
428 "children" => {
429 validate_arguments(&function.arguments, &Cardinality::Zero)?;
430
431 Ok(context.new_context_from(
432 context
433 .values
434 .iter()
435 .flat_map(|value| {
436 let result = value
437 .fields()
438 .iter()
439 .filter_map(|f| value.get_field(f).map(|v| v.flatten()))
440 .flatten()
441 .collect::<Vec<_>>();
442 result
443 })
444 .collect(),
445 ))
446 }
447 "repeat" => {
448 validate_arguments(&function.arguments, &Cardinality::One)?;
449
450 let projection = &function.arguments[0];
451 let mut end_result = vec![];
452 let mut cur = context;
453
454 while cur.values.len() != 0 {
455 cur = evaluate_expression(projection, cur, config.clone()).await?;
456 end_result.extend_from_slice(cur.values.as_slice());
457 }
458
459 Ok(cur.new_context_from(end_result))
460 }
461 "descendants" => {
462 validate_arguments(&function.arguments, &Cardinality::Zero)?;
463
464 let result = evaluate_expression(
466 &Expression::Singular(vec![Term::Invocation(Invocation::Function(
467 FunctionInvocation {
468 name: Identifier("repeat".to_string()),
469 arguments: vec![Expression::Singular(vec![Term::Invocation(
470 Invocation::Function(FunctionInvocation {
471 name: Identifier("children".to_string()),
472 arguments: vec![],
473 }),
474 )])],
475 },
476 ))]),
477 context,
478 config,
479 )
480 .await?;
481
482 Ok(result)
483 }
484 "type" => {
485 validate_arguments(&function.arguments, &Cardinality::Zero)?;
486
487 let mut next_ctx = Vec::with_capacity(context.values.len());
488 for v in context.values.iter() {
489 let type_name = v.fhir_type();
490 next_ctx.push(
491 context
492 .allocate_literal(Reflection {
493 name: type_name.to_string(),
494 })
495 .await,
496 );
497 }
498
499 Ok(context.new_context_from(next_ctx))
500 }
501 _ => {
502 return Err(FHIRPathError::NotImplemented(format!(
503 "Function '{}' is not implemented",
504 function.name.0
505 )));
506 }
507 }
508}
509
510fn equal_check<'b>(left: &Context<'b>, right: &Context<'b>) -> Result<bool, FHIRPathError> {
511 if NUMBER_TYPES.contains(left.values[0].fhir_type())
512 && NUMBER_TYPES.contains(right.values[0].fhir_type())
513 {
514 let left_value = downcast_number(left.values[0])?;
515 let right_value = downcast_number(right.values[0])?;
516 Ok(left_value == right_value)
517 } else if STRING_TYPES.contains(left.values[0].fhir_type())
518 && STRING_TYPES.contains(right.values[0].fhir_type())
519 {
520 let left_value = downcast_string(left.values[0])?;
521 let right_value = downcast_string(right.values[0])?;
522 Ok(left_value == right_value)
523 } else if BOOLEAN_TYPES.contains(left.values[0].fhir_type())
524 && BOOLEAN_TYPES.contains(right.values[0].fhir_type())
525 {
526 let left_value = downcast_bool(left.values[0])?;
527 let right_value = downcast_bool(right.values[0])?;
528 Ok(left_value == right_value)
529 } else {
530 Ok(false)
537 }
538}
539
540async fn evaluate_operation<'a>(
541 operation: &Operation,
542 context: Context<'a>,
543 config: Option<Arc<Config<'a>>>,
544) -> Result<Context<'a>, FHIRPathError> {
545 match operation {
546 Operation::Add(left, right) => {
547 operation_2(left, right, context, config, |left, right| {
548 Box::pin(async move {
549 if NUMBER_TYPES.contains(left.values[0].fhir_type())
550 && NUMBER_TYPES.contains(right.values[0].fhir_type())
551 {
552 let left_value = downcast_number(left.values[0])?;
553 let right_value = downcast_number(right.values[0])?;
554 Ok(left.new_context_from(vec![
555 left.allocate_literal(FHIRDecimal {
556 value: Some(left_value + right_value),
557 ..Default::default()
558 })
559 .await,
560 ]))
561 } else if STRING_TYPES.contains(left.values[0].fhir_type())
562 && STRING_TYPES.contains(right.values[0].fhir_type())
563 {
564 let left_string = downcast_string(left.values[0])?;
565 let right_string = downcast_string(right.values[0])?;
566
567 Ok(left.new_context_from(vec![
568 left.allocate_literal(FHIRString {
569 value: Some(left_string + &right_string),
570 ..Default::default()
571 })
572 .await,
573 ]))
574 } else {
575 Err(FHIRPathError::OperationError(OperationError::TypeMismatch(
576 left.values[0].fhir_type(),
577 right.values[0].fhir_type(),
578 )))
579 }
580 })
581 })
582 .await
583 }
584 Operation::Subtraction(left, right) => {
585 operation_2(left, right, context, config, |left, right| {
586 Box::pin(async move {
587 let left_value = downcast_number(left.values[0])?;
588 let right_value = downcast_number(right.values[0])?;
589
590 Ok(left.new_context_from(vec![
591 left.allocate_literal(FHIRDecimal {
592 value: Some(left_value - right_value),
593 ..Default::default()
594 })
595 .await,
596 ]))
597 })
598 })
599 .await
600 }
601 Operation::Multiplication(left, right) => {
602 operation_2(left, right, context, config, |left, right| {
603 Box::pin(async move {
604 let left_value = downcast_number(left.values[0])?;
605 let right_value = downcast_number(right.values[0])?;
606
607 Ok(left.new_context_from(vec![
608 left.allocate_literal(FHIRDecimal {
609 value: Some(left_value * right_value),
610 ..Default::default()
611 })
612 .await,
613 ]))
614 })
615 })
616 .await
617 }
618 Operation::Division(left, right) => {
619 operation_2(left, right, context, config, |left, right| {
620 Box::pin(async move {
621 let left_value = downcast_number(left.values[0])?;
622 let right_value = downcast_number(right.values[0])?;
623
624 Ok(left.new_context_from(vec![
625 left.allocate_literal(FHIRDecimal {
626 value: Some(left_value / right_value),
627 ..Default::default()
628 })
629 .await,
630 ]))
631 })
632 })
633 .await
634 }
635 Operation::Equal(left, right) => {
636 operation_2(left, right, context, config, |left, right| {
637 Box::pin(async move {
638 let are_equal = FHIRBoolean {
639 value: Some(equal_check(&left, &right)?),
640 ..Default::default()
641 };
642 Ok(left.new_context_from(vec![left.allocate_literal(are_equal).await]))
643 })
644 })
645 .await
646 }
647 Operation::NotEqual(left, right) => {
648 operation_2(left, right, context, config, |left, right| {
649 Box::pin(async move {
650 let not_equal = FHIRBoolean {
651 value: Some(!equal_check(&left, &right)?),
652 ..Default::default()
653 };
654 Ok(left.new_context_from(vec![left.allocate_literal(not_equal).await]))
655 })
656 })
657 .await
658 }
659 Operation::And(left, right) => {
660 operation_2(left, right, context, config, |left, right| {
661 Box::pin(async move {
662 let left_value = downcast_bool(left.values[0])?;
663 let right_value = downcast_bool(right.values[0])?;
664
665 Ok(left.new_context_from(vec![
666 left.allocate_literal(FHIRBoolean {
667 value: Some(left_value && right_value),
668 ..Default::default()
669 })
670 .await,
671 ]))
672 })
673 })
674 .await
675 }
676 Operation::Or(left, right) => {
677 operation_2(left, right, context, config, |left, right| {
678 Box::pin(async move {
679 let left_value = downcast_bool(left.values[0])?;
680 let right_value = downcast_bool(right.values[0])?;
681
682 Ok(left.new_context_from(vec![
683 left.allocate_literal(FHIRBoolean {
684 value: Some(left_value || right_value),
685 ..Default::default()
686 })
687 .await,
688 ]))
689 })
690 })
691 .await
692 }
693 Operation::Union(left, right) => {
694 operation_n(left, right, context, config, |left, right| {
695 let mut union = vec![];
696 union.extend(left.values.iter());
697 union.extend(right.values.iter());
698 Ok(left.new_context_from(union))
699 })
700 .await
701 }
702 Operation::Modulo(_, _) => Err(FHIRPathError::NotImplemented("Modulo".to_string())),
703 Operation::Is(expression, type_name) => {
704 let left = evaluate_expression(expression, context, config).await?;
705 if left.values.len() > 1 {
706 Err(FHIRPathError::OperationError(
707 OperationError::InvalidCardinality,
708 ))
709 } else {
710 if let Some(type_name) = type_name.0.get(0).as_ref().map(|k| &k.0) {
711 let next_context = filter_by_type(&type_name, &left);
712 Ok(left.new_context_from(vec![
713 left.allocate_literal(FHIRBoolean {
714 value: Some(!next_context.values.is_empty()),
715 ..Default::default()
716 })
717 .await,
718 ]))
719 } else {
720 Ok(left.new_context_from(vec![]))
721 }
722 }
723 }
724 Operation::As(expression, type_name) => {
725 let left = evaluate_expression(expression, context, config).await?;
726 if left.values.len() > 1 {
727 Err(FHIRPathError::OperationError(
728 OperationError::InvalidCardinality,
729 ))
730 } else {
731 if let Some(type_name) = type_name.0.get(0).as_ref().map(|k| &k.0) {
732 Ok(filter_by_type(&type_name, &left))
733 } else {
734 Ok(left.new_context_from(vec![]))
735 }
736 }
737 }
738 Operation::Polarity(_, _) => Err(FHIRPathError::NotImplemented("Polarity".to_string())),
739 Operation::DivisionTruncated(_, _) => Err(FHIRPathError::NotImplemented(
740 "DivisionTruncated".to_string(),
741 )),
742 Operation::LessThan(_, _) => Err(FHIRPathError::NotImplemented("LessThan".to_string())),
743 Operation::GreaterThan(_, _) => {
744 Err(FHIRPathError::NotImplemented("GreaterThan".to_string()))
745 }
746 Operation::LessThanEqual(_, _) => {
747 Err(FHIRPathError::NotImplemented("LessThanEqual".to_string()))
748 }
749 Operation::GreaterThanEqual(_, _) => Err(FHIRPathError::NotImplemented(
750 "GreaterThanEqual".to_string(),
751 )),
752 Operation::Equivalent(_, _) => Err(FHIRPathError::NotImplemented("Equivalent".to_string())),
753
754 Operation::NotEquivalent(_, _) => {
755 Err(FHIRPathError::NotImplemented("NotEquivalent".to_string()))
756 }
757 Operation::In(_left, _right) => Err(FHIRPathError::NotImplemented("In".to_string())),
758 Operation::Contains(_left, _right) => {
759 Err(FHIRPathError::NotImplemented("Contains".to_string()))
760 }
761 Operation::XOr(left, right) => {
762 operation_2(left, right, context, config, |left, right| {
763 Box::pin(async move {
764 let left_value = downcast_bool(left.values[0])?;
765 let right_value = downcast_bool(right.values[0])?;
766
767 Ok(left.new_context_from(vec![
768 left.allocate_literal(FHIRBoolean {
769 value: Some(left_value ^ right_value),
770 ..Default::default()
771 })
772 .await,
773 ]))
774 })
775 })
776 .await
777 }
778 Operation::Implies(_left, _right) => {
779 Err(FHIRPathError::NotImplemented("Implies".to_string()))
780 }
781 }
782}
783
784fn evaluate_expression<'a>(
785 ast: &Expression,
786 context: Context<'a>,
787 config: Option<Arc<Config<'a>>>,
788) -> Pin<Box<impl Future<Output = Result<Context<'a>, FHIRPathError>>>> {
789 Box::pin(async move {
790 match ast {
791 Expression::Operation(operation) => {
792 evaluate_operation(operation, context, config).await
793 }
794 Expression::Singular(singular_ast) => {
795 evaluate_singular(singular_ast, context, config).await
796 }
797 }
798 })
799}
800
801#[derive(Debug)]
802pub enum ResolvedValue {
803 Box(Box<dyn MetaValue>),
804 Arc(Arc<dyn MetaValue>),
805}
806
807impl ResolvedValue {
808 pub fn as_ref(&self) -> &dyn MetaValue {
809 match self {
810 ResolvedValue::Box(b) => &**b,
811 ResolvedValue::Arc(a) => &**a,
812 }
813 }
814}
815
816pub struct Context<'a> {
817 allocator: Arc<Mutex<allocators::bumpalo::Allocator>>,
818 values: Vec<&'a dyn MetaValue>,
819}
820
821pub enum ExternalConstantResolver<'a> {
822 Function(
823 Box<
824 dyn Fn(String) -> Pin<Box<dyn Future<Output = Option<ResolvedValue>> + Send>>
825 + Send
826 + Sync,
827 >,
828 ),
829 Variable(HashMap<String, &'a dyn MetaValue>),
830}
831
832pub struct Config<'a> {
833 pub variable_resolver: Option<ExternalConstantResolver<'a>>,
834}
835
836async fn resolve_external_constant<'a>(
837 name: &str,
838 resolver: Option<&ExternalConstantResolver<'a>>,
839 context: Context<'a>,
840) -> Result<Context<'a>, FHIRPathError> {
841 let external_constant = match resolver {
842 Some(ExternalConstantResolver::Function(func)) => {
843 let result = func(name.to_string()).await;
844
845 if let Some(result) = result {
846 Some(context.allocate(result).await)
847 } else {
848 None
849 }
850 }
851 Some(ExternalConstantResolver::Variable(map)) => map.get(name).map(|s| *s),
852 None => None,
853 };
854
855 if let Some(result) = external_constant {
856 return Ok(context.new_context_from(vec![result]));
857 } else {
858 return Ok(context.new_context_from(vec![]));
859 }
860}
861
862impl<'a> Context<'a> {
863 fn new(
864 values: Vec<&'a dyn MetaValue>,
865 allocator: Arc<Mutex<allocators::bumpalo::Allocator>>,
866 ) -> Self {
867 Self {
868 allocator,
869 values: values,
870 }
871 }
872 fn new_context_from(&self, values: Vec<&'a dyn MetaValue>) -> Self {
873 Self {
874 allocator: self.allocator.clone(),
875 values: values,
876 }
877 }
878 async fn allocate(&self, value: ResolvedValue) -> &'a dyn MetaValue {
879 self.allocator.lock().await.allocate_resolved(value)
880 }
881
882 async fn allocate_literal<T: MetaValue>(&self, value: T) -> &'a dyn MetaValue {
883 self.allocator.lock().await.allocate_literal(value)
884 }
885 pub fn iter(&'a self) -> Box<dyn Iterator<Item = &'a dyn MetaValue> + 'a> {
886 Box::new(self.values.iter().map(|v| *v))
887 }
888}
889
890impl Clone for Context<'_> {
891 fn clone(&self) -> Self {
892 Self {
893 allocator: self.allocator.clone(),
894 values: self.values.clone(),
895 }
896 }
897}
898
899pub struct FPEngine {}
900
901static AST: LazyLock<Arc<DashMap<String, Expression>>> = LazyLock::new(|| Arc::new(DashMap::new()));
902
903fn get_ast(
904 path: &str,
905) -> Result<dashmap::mapref::one::Ref<'static, String, Expression>, FHIRPathError> {
906 let ast: dashmap::mapref::one::Ref<'_, String, Expression> =
907 if let Some(expression_ast) = AST.get(path) {
908 expression_ast
909 } else {
910 AST.insert(path.to_string(), parser::parse(path)?);
911 let expression_ast = AST.get(path).ok_or_else(|| {
912 FHIRPathError::InternalError("Failed to find path post insert".to_string())
913 })?;
914 expression_ast
915 };
916
917 Ok(ast)
918}
919
920impl FPEngine {
921 pub fn new() -> Self {
922 Self {}
923 }
924
925 pub async fn evaluate<'a, 'b>(
930 &self,
931 path: &str,
932 values: Vec<&'a dyn MetaValue>,
933 ) -> Result<Context<'b>, FHIRPathError>
934 where
935 'a: 'b,
936 {
937 let ast = get_ast(path)?;
938
939 let allocator: Arc<Mutex<allocators::bumpalo::Allocator>> =
941 Arc::new(Mutex::new(allocators::bumpalo::Allocator::new()));
942
943 let context = Context::new(values, allocator.clone());
944
945 let result = evaluate_expression(&ast, context, None).await?;
946 Ok(result)
947 }
948
949 pub async fn evaluate_with_config<'a, 'b>(
955 &self,
956 path: &str,
957 values: Vec<&'a dyn MetaValue>,
958 config: Arc<Config<'b>>,
959 ) -> Result<Context<'b>, FHIRPathError>
960 where
961 'a: 'b,
962 {
963 let ast = get_ast(path)?;
964
965 let allocator = Arc::new(Mutex::new(allocators::bumpalo::Allocator::new()));
967
968 let context = Context::new(values, allocator.clone());
969
970 let result = evaluate_expression(&ast, context, Some(config)).await?;
971
972 Ok(result)
973 }
974}
975
976#[cfg(test)]
977mod tests {
978 use super::*;
979 use haste_fhir_model::r4::{
980 datetime::DateTime,
981 generated::{
982 resources::{
983 Bundle, Patient, PatientDeceasedTypeChoice, PatientLink, Resource, SearchParameter,
984 },
985 types::{
986 Extension, ExtensionValueTypeChoice, FHIRDateTime, FHIRString, FHIRUri, HumanName,
987 Identifier, Reference,
988 },
989 },
990 };
991
992 use haste_reflect_derive::Reflect;
993
994 #[derive(Reflect, Debug)]
995 #[fhir_type = "BackboneElement"]
996 struct C {
997 c: String,
998 }
999
1000 #[derive(Reflect, Debug)]
1001 #[fhir_type = "BackboneElement"]
1002 struct B {
1003 b: Vec<Box<C>>,
1004 }
1005
1006 #[derive(Reflect, Debug)]
1007 #[fhir_type = "BackboneElement"]
1008 struct A {
1009 a: Vec<Box<B>>,
1010 }
1011
1012 fn load_search_parameters() -> Vec<SearchParameter> {
1013 let json =
1014 include_str!("../../artifacts/artifacts/r4/hl7/minified/search-parameters.min.json");
1015 let bundle = serde_json::from_str::<Bundle>(json).unwrap();
1016
1017 let search_parameters: Vec<SearchParameter> = bundle
1018 .entry
1019 .unwrap_or_else(|| Vec::new())
1020 .into_iter()
1021 .map(|e| e.resource)
1022 .filter(|e| e.is_some())
1023 .filter_map(|e| match e {
1024 Some(k) => match *k {
1025 Resource::SearchParameter(sp) => Some(sp),
1026 _ => None,
1027 },
1028 _ => None,
1029 })
1030 .collect();
1031
1032 search_parameters
1033 }
1034
1035 #[tokio::test]
1036 async fn filter_typechoice_test() {
1037 let patient = Patient {
1038 id: Some("patient-id".to_string()),
1039 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1040 value: Some(true),
1041 ..Default::default()
1042 }))),
1043 ..Default::default()
1044 };
1045
1046 let engine = FPEngine::new();
1047 let result = engine
1048 .evaluate("(Patient.deceased.ofType(dateTime))", vec![&patient])
1049 .await
1050 .unwrap();
1051
1052 assert_eq!(result.values.len(), 0);
1053
1054 let result = engine
1055 .evaluate("(Patient.deceased.ofType(boolean))", vec![&patient])
1056 .await
1057 .unwrap();
1058
1059 let value = result.values[0];
1060 let boolean_value: &FHIRBoolean = value
1061 .as_any()
1062 .downcast_ref::<FHIRBoolean>()
1063 .expect("Failed to downcast to FHIRBoolean");
1064
1065 assert_eq!(boolean_value.value, Some(true));
1066
1067 let patient = Patient {
1068 id: Some("patient-id".to_string()),
1069 deceased: Some(PatientDeceasedTypeChoice::DateTime(Box::new(
1070 FHIRDateTime {
1071 value: Some(DateTime::Year(1980)),
1072 ..Default::default()
1073 },
1074 ))),
1075 ..Default::default()
1076 };
1077
1078 let result = engine
1079 .evaluate("(Patient.deceased.ofType(boolean))", vec![&patient])
1080 .await
1081 .unwrap();
1082
1083 assert_eq!(result.values.len(), 0);
1084
1085 let result = engine
1086 .evaluate("(Patient.deceased.ofType(dateTime))", vec![&patient])
1087 .await
1088 .unwrap();
1089
1090 assert_eq!(result.values.len(), 1);
1091
1092 let value = result.values[0];
1093 let datetime_value: &FHIRDateTime = value
1094 .as_any()
1095 .downcast_ref::<FHIRDateTime>()
1096 .expect("Failed to downcast to FHIRDateTime");
1097
1098 assert_eq!(datetime_value.value, Some(DateTime::Year(1980)));
1099 }
1100
1101 #[tokio::test]
1102 async fn test_variable_resolution() {
1103 let engine = FPEngine::new();
1104 let patient = Patient {
1105 id: Some("my-patient".to_string()),
1106 ..Default::default()
1107 };
1108 let config = Arc::new(Config {
1109 variable_resolver: Some(ExternalConstantResolver::Variable(
1110 vec![("patient".to_string(), &patient as &dyn MetaValue)]
1111 .into_iter()
1112 .collect(),
1113 )),
1114 });
1115
1116 let result = engine
1117 .evaluate_with_config("%patient", vec![], config.clone())
1118 .await
1119 .unwrap();
1120
1121 assert_eq!(result.values.len(), 1);
1122 let p = result.values[0].as_any().downcast_ref::<Patient>().unwrap();
1123
1124 assert_eq!(p.id, patient.id);
1125
1126 let result_failed = engine
1127 .evaluate_with_config("%nobody", vec![], config)
1128 .await
1129 .unwrap();
1130
1131 assert_eq!(result_failed.values.len(), 0);
1132 }
1133
1134 #[tokio::test]
1135 async fn test_where_clause() {
1136 let engine = FPEngine::new();
1137 let mut patient = Patient::default();
1138 let mut identifier = Identifier::default();
1139 let extension = Extension {
1140 id: None,
1141 url: "test-extension".to_string(),
1142 extension: None,
1143 value: Some(ExtensionValueTypeChoice::String(Box::new(FHIRString {
1144 id: None,
1145 extension: None,
1146 value: Some("example value".to_string()),
1147 }))),
1148 };
1149 identifier.value = Some(Box::new(FHIRString {
1150 id: None,
1151 extension: Some(vec![Box::new(extension)]),
1152 value: Some("12345".to_string()),
1153 }));
1154 patient.identifier_ = Some(vec![Box::new(identifier)]);
1155
1156 let context = engine
1157 .evaluate(
1158 "$this.identifier.value.where($this.extension.value.exists())",
1159 vec![&patient],
1160 )
1161 .await;
1162
1163 assert_eq!(context.unwrap().values.len(), 1);
1164
1165 let context = engine
1166 .evaluate(
1167 "$this.identifier.value.where($this.extension.extension.exists())",
1168 vec![&patient],
1169 )
1170 .await;
1171 assert_eq!(context.unwrap().values.len(), 0);
1172 }
1173
1174 #[tokio::test]
1175 async fn test_all_parameters() {
1176 let search_parameters = load_search_parameters();
1177 for param in search_parameters.iter() {
1178 if let Some(expression) = ¶m.expression {
1179 let engine = FPEngine::new();
1180 let context = engine
1181 .evaluate(expression.value.as_ref().unwrap().as_str(), vec![])
1182 .await;
1183
1184 if let Err(err) = context {
1185 panic!(
1186 "Failed to evaluate search parameter '{}': {}",
1187 expression.value.as_ref().unwrap(),
1188 err
1189 );
1190 }
1191 }
1192 }
1193 }
1194
1195 fn test_patient() -> Patient {
1196 let mut patient = Patient::default();
1197 let mut name = HumanName::default();
1198 name.given = Some(vec![Box::new(FHIRString {
1199 id: None,
1200 extension: None,
1201 value: Some("Bob".to_string()),
1202 })]);
1203
1204 let mut mrn_identifier = Identifier::default();
1205 mrn_identifier.value = Some(Box::new(FHIRString {
1206 id: None,
1207 extension: None,
1208 value: Some("mrn-12345".to_string()),
1209 }));
1210 mrn_identifier.system = Some(Box::new(FHIRUri {
1211 id: None,
1212 extension: None,
1213 value: Some("mrn".to_string()),
1214 }));
1215
1216 let mut ssn_identifier = Identifier::default();
1217 ssn_identifier.value = Some(Box::new(FHIRString {
1218 id: None,
1219 extension: None,
1220 value: Some("ssn-12345".to_string()),
1221 }));
1222 ssn_identifier.system = Some(Box::new(FHIRUri {
1223 id: None,
1224 extension: None,
1225 value: Some("ssn".to_string()),
1226 }));
1227
1228 mrn_identifier.system = Some(Box::new(FHIRUri {
1229 id: None,
1230 extension: None,
1231 value: Some("mrn".to_string()),
1232 }));
1233
1234 patient.identifier_ = Some(vec![Box::new(mrn_identifier), Box::new(ssn_identifier)]);
1235 patient.name = Some(vec![Box::new(name)]);
1236 patient
1237 }
1238
1239 #[tokio::test]
1240 async fn indexing_tests() {
1241 let engine = FPEngine::new();
1242 let patient = test_patient();
1243
1244 let given_name = engine
1245 .evaluate("$this.name.given[0]", vec![&patient])
1246 .await
1247 .unwrap();
1248
1249 assert_eq!(given_name.values.len(), 1);
1250 let value = given_name.values[0];
1251 let name: &FHIRString = value
1252 .as_any()
1253 .downcast_ref::<FHIRString>()
1254 .expect("Failed to downcast to FHIRString");
1255
1256 assert_eq!(name.value.as_deref(), Some("Bob"));
1257
1258 let ssn_identifier = engine
1259 .evaluate("$this.identifier[1]", vec![&patient])
1260 .await
1261 .unwrap();
1262
1263 assert_eq!(ssn_identifier.values.len(), 1);
1264 let value = ssn_identifier.values[0];
1265 let identifier: &Identifier = value
1266 .as_any()
1267 .downcast_ref::<Identifier>()
1268 .expect("Failed to downcast to Identifier");
1269
1270 assert_eq!(
1271 identifier.value.as_ref().unwrap().value.as_deref(),
1272 Some("ssn-12345")
1273 );
1274
1275 let all_identifiers = engine
1276 .evaluate("$this.identifier", vec![&patient])
1277 .await
1278 .unwrap();
1279 assert_eq!(all_identifiers.values.len(), 2);
1280 }
1281
1282 #[tokio::test]
1283 async fn where_testing() {
1284 let engine = FPEngine::new();
1285 let patient = test_patient();
1286
1287 let name_where_clause = engine
1288 .evaluate(
1289 "$this.name.given.where($this.value = 'Bob')",
1290 vec![&patient],
1291 )
1292 .await
1293 .unwrap();
1294
1295 assert_eq!(name_where_clause.values.len(), 1);
1296 let value = name_where_clause.values[0];
1297 let name: &FHIRString = value
1298 .as_any()
1299 .downcast_ref::<FHIRString>()
1300 .expect("Failed to downcast to FHIRString");
1301
1302 assert_eq!(name.value.as_deref(), Some("Bob"));
1303
1304 let ssn_identifier_clause = engine
1305 .evaluate(
1306 "$this.identifier.where($this.system.value = 'ssn')",
1307 vec![&patient],
1308 )
1309 .await
1310 .unwrap();
1311 assert_eq!(ssn_identifier_clause.values.len(), 1);
1312
1313 let ssn_identifier = ssn_identifier_clause.values[0]
1314 .as_any()
1315 .downcast_ref::<Identifier>()
1316 .expect("Failed to downcast to Identifier");
1317
1318 assert_eq!(
1319 ssn_identifier.value.as_ref().unwrap().value.as_deref(),
1320 Some("ssn-12345")
1321 );
1322 }
1323
1324 #[tokio::test]
1325 async fn test_equality() {
1326 let engine = FPEngine::new();
1327
1328 let string_equal = engine.evaluate("'test' = 'test'", vec![]).await.unwrap();
1330 for r in string_equal.iter() {
1331 let b: bool = r
1332 .as_any()
1333 .downcast_ref::<FHIRBoolean>()
1334 .unwrap()
1335 .value
1336 .unwrap()
1337 .clone();
1338 assert_eq!(b, true);
1339 }
1340 let string_unequal = engine.evaluate("'invalid' = 'test'", vec![]).await.unwrap();
1341 for r in string_unequal.iter() {
1342 let b: bool = r
1343 .as_any()
1344 .downcast_ref::<FHIRBoolean>()
1345 .unwrap()
1346 .value
1347 .unwrap()
1348 .clone();
1349 assert_eq!(b, false);
1350 }
1351
1352 let number_equal = engine.evaluate("12 = 12", vec![]).await.unwrap();
1354 for r in number_equal.iter() {
1355 let b: bool = r
1356 .as_any()
1357 .downcast_ref::<FHIRBoolean>()
1358 .unwrap()
1359 .value
1360 .unwrap()
1361 .clone();
1362 assert_eq!(b, true);
1363 }
1364 let number_unequal = engine.evaluate("13 = 12", vec![]).await.unwrap();
1365 for r in number_unequal.iter() {
1366 let b: bool = r
1367 .as_any()
1368 .downcast_ref::<FHIRBoolean>()
1369 .unwrap()
1370 .value
1371 .unwrap()
1372 .clone();
1373 assert_eq!(b, false);
1374 }
1375
1376 let bool_equal = engine.evaluate("false = false", vec![]).await.unwrap();
1378 for r in bool_equal.iter() {
1379 let b: bool = r
1380 .as_any()
1381 .downcast_ref::<FHIRBoolean>()
1382 .unwrap()
1383 .value
1384 .unwrap()
1385 .clone();
1386 assert_eq!(b, true);
1387 }
1388 let bool_unequal = engine.evaluate("false = true", vec![]).await.unwrap();
1389 for r in bool_unequal.iter() {
1390 let b: bool = r
1391 .as_any()
1392 .downcast_ref::<FHIRBoolean>()
1393 .unwrap()
1394 .value
1395 .unwrap()
1396 .clone();
1397 assert_eq!(b, false);
1398 }
1399
1400 let bool_equal = engine.evaluate("12 = 13 = false", vec![]).await.unwrap();
1402 for r in bool_equal.iter() {
1403 let b: bool = r
1404 .as_any()
1405 .downcast_ref::<FHIRBoolean>()
1406 .unwrap()
1407 .value
1408 .unwrap()
1409 .clone();
1410 assert_eq!(b, true);
1411 }
1412 let bool_unequal = engine.evaluate("12 = 13 = true", vec![]).await.unwrap();
1413 for r in bool_unequal.iter() {
1414 let b: bool = r
1415 .as_any()
1416 .downcast_ref::<FHIRBoolean>()
1417 .unwrap()
1418 .value
1419 .unwrap()
1420 .clone();
1421 assert_eq!(b, false);
1422 }
1423 let bool_unequal = engine.evaluate("12 = (13 - 1)", vec![]).await.unwrap();
1424 for r in bool_unequal.iter() {
1425 let b: bool = r
1426 .as_any()
1427 .downcast_ref::<FHIRBoolean>()
1428 .unwrap()
1429 .value
1430 .unwrap()
1431 .clone();
1432
1433 assert_eq!(b, true);
1434 }
1435 }
1436
1437 #[tokio::test]
1438 async fn test_string_concat() {
1439 let engine = FPEngine::new();
1440 let patient = test_patient();
1441
1442 let simple_result = engine.evaluate("'Hello' + ' World'", vec![]).await.unwrap();
1443 for r in simple_result.iter() {
1444 let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1445 assert_eq!(s.value, Some("Hello World".to_string()));
1446 }
1447
1448 let simple_result = engine
1449 .evaluate("$this.name.given + ' Miller'", vec![&patient])
1450 .await
1451 .unwrap();
1452 for r in simple_result.iter() {
1453 let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1454 assert_eq!(s.value, Some("Bob Miller".to_string()));
1455 }
1456 }
1457
1458 #[tokio::test]
1459 async fn test_simple() {
1460 let root = A {
1461 a: vec![Box::new(B {
1462 b: vec![Box::new(C {
1463 c: "whatever".to_string(),
1464 })],
1465 })],
1466 };
1467
1468 let engine = FPEngine::new();
1469 let result = engine.evaluate("a.b.c", vec![&root]).await.unwrap();
1470
1471 let strings: Vec<&String> = result
1472 .iter()
1473 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1474 .collect();
1475
1476 assert_eq!(strings, vec!["whatever"]);
1477 }
1478
1479 #[tokio::test]
1480 async fn allocation() {
1481 let engine = FPEngine::new();
1482 let result = engine.evaluate("'asdf'", vec![]).await.unwrap();
1483
1484 for r in result.iter() {
1485 let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1486
1487 assert_eq!(s.value, Some("asdf".to_string()));
1488 }
1489 }
1490
1491 #[tokio::test]
1492 async fn order_operation() {
1493 let engine = FPEngine::new();
1494 let result = engine.evaluate("45 + 2 * 3", vec![]).await.unwrap();
1495
1496 for r in result.iter() {
1497 let s = r.as_any().downcast_ref::<FHIRDecimal>().unwrap().clone();
1498
1499 assert_eq!(s.value, Some(51.0));
1500 }
1501 }
1502
1503 #[tokio::test]
1504 async fn xor_operation() {
1505 let engine = FPEngine::new();
1506 let result = engine.evaluate("true xor true", vec![]).await.unwrap();
1507
1508 for r in result.iter() {
1509 let b: bool = r
1510 .as_any()
1511 .downcast_ref::<FHIRBoolean>()
1512 .unwrap()
1513 .value
1514 .unwrap()
1515 .clone();
1516
1517 assert_eq!(b, false);
1518 }
1519 }
1520
1521 #[tokio::test]
1522 async fn domain_resource_filter() {
1523 let engine = FPEngine::new();
1524
1525 let patient =
1526 serde_json::from_str::<Resource>(r#"{"id": "patient-id", "resourceType": "Patient"}"#)
1527 .unwrap();
1528 let result = engine
1529 .evaluate("Resource.id", vec![&patient])
1530 .await
1531 .unwrap();
1532 let ids: Vec<&String> = result
1533 .iter()
1534 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1535 .collect();
1536
1537 assert_eq!(ids.len(), 1);
1538 assert_eq!(ids[0], "patient-id");
1539
1540 let result2 = engine
1541 .evaluate("DomainResource.id", vec![&patient])
1542 .await
1543 .unwrap();
1544 let ids2: Vec<&String> = result2
1545 .iter()
1546 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1547 .collect();
1548 assert_eq!(ids2.len(), 1);
1549 assert_eq!(ids2[0], "patient-id");
1550 }
1551
1552 #[tokio::test]
1553 async fn type_test() {
1554 let engine = FPEngine::new();
1555 let patient = Patient::default();
1556
1557 let result = engine
1558 .evaluate("$this.type().name", vec![&patient])
1559 .await
1560 .unwrap();
1561 let ids: Vec<&String> = result
1562 .iter()
1563 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1564 .collect();
1565
1566 assert_eq!(ids.len(), 1);
1567 assert_eq!(ids[0], "Patient");
1568 }
1569
1570 #[tokio::test]
1571 async fn resolve_test() {
1572 let engine = FPEngine::new();
1573 let observation = serde_json::from_str::<Resource>(r#"
1574 {
1575 "resourceType": "Observation",
1576 "id": "f001",
1577 "text": {
1578 "status": "generated",
1579 "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>"
1580 },
1581 "identifier": [
1582 {
1583 "use": "official",
1584 "system": "http://www.bmc.nl/zorgportal/identifiers/observations",
1585 "value": "6323"
1586 }
1587 ],
1588 "status": "final",
1589 "code": {
1590 "coding": [
1591 {
1592 "system": "http://loinc.org",
1593 "code": "15074-8",
1594 "display": "Glucose [Moles/volume] in Blood"
1595 }
1596 ]
1597 },
1598 "subject": {
1599 "reference": "Patient/f001",
1600 "display": "P. van de Heuvel"
1601 },
1602 "effectivePeriod": {
1603 "start": "2013-04-02T09:30:10+01:00"
1604 },
1605 "issued": "2013-04-03T15:30:10+01:00",
1606 "performer": [
1607 {
1608 "reference": "Practitioner/f005",
1609 "display": "A. Langeveld"
1610 }
1611 ],
1612 "valueQuantity": {
1613 "value": 6.3,
1614 "unit": "mmol/l",
1615 "system": "http://unitsofmeasure.org",
1616 "code": "mmol/L"
1617 },
1618 "interpretation": [
1619 {
1620 "coding": [
1621 {
1622 "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
1623 "code": "H",
1624 "display": "High"
1625 }
1626 ]
1627 }
1628 ],
1629 "referenceRange": [
1630 {
1631 "low": {
1632 "value": 3.1,
1633 "unit": "mmol/l",
1634 "system": "http://unitsofmeasure.org",
1635 "code": "mmol/L"
1636 },
1637 "high": {
1638 "value": 6.2,
1639 "unit": "mmol/l",
1640 "system": "http://unitsofmeasure.org",
1641 "code": "mmol/L"
1642 }
1643 }
1644 ]
1645 }
1646 "#).unwrap();
1647
1648 let result = engine
1649 .evaluate(
1650 "Observation.subject.where(resolve() is Patient)",
1651 vec![&observation],
1652 )
1653 .await
1654 .unwrap();
1655
1656 let references: Vec<&Reference> = result
1657 .iter()
1658 .map(|r| r.as_any().downcast_ref::<Reference>().unwrap())
1659 .collect();
1660
1661 assert_eq!(references.len(), 1);
1662 assert_eq!(
1663 references[0].reference.as_ref().unwrap().value,
1664 Some("Patient/f001".to_string())
1665 );
1666 }
1667
1668 #[tokio::test]
1669 async fn children_test() {
1670 let engine = FPEngine::new();
1671 let patient = Patient {
1672 name: Some(vec![Box::new(HumanName {
1673 given: Some(vec![Box::new(FHIRString {
1674 value: Some("Alice".to_string()),
1675 ..Default::default()
1676 })]),
1677 ..Default::default()
1678 })]),
1679 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1680 value: Some(true),
1681 ..Default::default()
1682 }))),
1683 ..Default::default()
1684 };
1685
1686 let result = engine
1687 .evaluate("$this.children()", vec![&patient])
1688 .await
1689 .unwrap();
1690
1691 assert_eq!(result.values.len(), 2);
1692 assert_eq!(
1693 result
1694 .values
1695 .iter()
1696 .map(|v| v.fhir_type())
1697 .collect::<Vec<_>>(),
1698 vec!["HumanName", "boolean"]
1699 );
1700 }
1701
1702 #[tokio::test]
1703 async fn repeat_test() {
1704 let engine = FPEngine::new();
1705 let patient = Patient {
1706 name: Some(vec![Box::new(HumanName {
1707 given: Some(vec![Box::new(FHIRString {
1708 value: Some("Alice".to_string()),
1709 ..Default::default()
1710 })]),
1711 ..Default::default()
1712 })]),
1713 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1714 value: Some(true),
1715 ..Default::default()
1716 }))),
1717 ..Default::default()
1718 };
1719
1720 let result = engine
1721 .evaluate("$this.name.given", vec![&patient])
1722 .await
1723 .unwrap();
1724
1725 assert_eq!(result.values.len(), 1);
1726
1727 assert_eq!(result.values[0].fhir_type(), "string");
1728
1729 let result = engine
1730 .evaluate("$this.repeat(children())", vec![&patient])
1731 .await
1732 .unwrap();
1733
1734 assert_eq!(
1735 result
1736 .values
1737 .iter()
1738 .map(|v| v.fhir_type())
1739 .collect::<Vec<_>>(),
1740 vec![
1741 "HumanName",
1742 "boolean",
1743 "string",
1744 "http://hl7.org/fhirpath/System.Boolean",
1745 "http://hl7.org/fhirpath/System.String"
1746 ]
1747 );
1748 }
1749 #[tokio::test]
1750 async fn descendants_test() {
1751 let engine = FPEngine::new();
1752 let patient = Patient {
1753 name: Some(vec![Box::new(HumanName {
1754 given: Some(vec![Box::new(FHIRString {
1755 value: Some("Alice".to_string()),
1756 ..Default::default()
1757 })]),
1758 ..Default::default()
1759 })]),
1760 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1761 value: Some(true),
1762 ..Default::default()
1763 }))),
1764 ..Default::default()
1765 };
1766 let result = engine
1767 .evaluate("descendants()", vec![&patient])
1768 .await
1769 .unwrap();
1770
1771 assert_eq!(
1772 result
1773 .values
1774 .iter()
1775 .map(|v| v.fhir_type())
1776 .collect::<Vec<_>>(),
1777 vec![
1778 "HumanName",
1779 "boolean",
1780 "string",
1781 "http://hl7.org/fhirpath/System.Boolean",
1782 "http://hl7.org/fhirpath/System.String"
1783 ]
1784 );
1785 }
1786
1787 #[tokio::test]
1788 async fn descendants_test_filter() {
1789 let engine = FPEngine::new();
1790 let patient = Patient {
1791 link: Some(vec![PatientLink {
1792 other: Box::new(Reference {
1793 reference: Some(Box::new(FHIRString {
1794 value: Some("Patient/123".to_string()),
1795 ..Default::default()
1796 })),
1797 ..Default::default()
1798 }),
1799 ..Default::default()
1800 }]),
1801 name: Some(vec![Box::new(HumanName {
1802 given: Some(vec![Box::new(FHIRString {
1803 value: Some("Alice".to_string()),
1804 ..Default::default()
1805 })]),
1806 ..Default::default()
1807 })]),
1808 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1809 value: Some(true),
1810 ..Default::default()
1811 }))),
1812 ..Default::default()
1813 };
1814 let result = engine
1815 .evaluate("descendants()", vec![&patient])
1816 .await
1817 .unwrap();
1818
1819 assert_eq!(
1820 result
1821 .values
1822 .iter()
1823 .map(|v| v.fhir_type())
1824 .collect::<Vec<_>>(),
1825 vec![
1826 "HumanName",
1827 "boolean",
1828 "BackboneElement",
1829 "string",
1830 "http://hl7.org/fhirpath/System.Boolean",
1831 "Reference",
1832 "http://hl7.org/fhirpath/System.String",
1833 "string",
1834 "http://hl7.org/fhirpath/System.String"
1835 ]
1836 );
1837
1838 let result = engine
1839 .evaluate("descendants().ofType(Reference)", vec![&patient])
1840 .await
1841 .unwrap();
1842
1843 assert_eq!(
1844 result
1845 .values
1846 .iter()
1847 .map(|v| v.fhir_type())
1848 .collect::<Vec<_>>(),
1849 vec!["Reference",]
1850 );
1851
1852 let value = result.values[0]
1853 .as_any()
1854 .downcast_ref::<Reference>()
1855 .unwrap();
1856
1857 assert_eq!(
1858 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
1859 "Patient/123"
1860 );
1861 }
1862
1863 #[tokio::test]
1864 async fn try_unsafe_set_from_ref() {
1865 let engine = FPEngine::new();
1866 let patient = Patient {
1867 link: Some(vec![PatientLink {
1868 other: Box::new(Reference {
1869 reference: Some(Box::new(FHIRString {
1870 value: Some("Patient/123".to_string()),
1871 ..Default::default()
1872 })),
1873 ..Default::default()
1874 }),
1875 ..Default::default()
1876 }]),
1877 name: Some(vec![Box::new(HumanName {
1878 given: Some(vec![Box::new(FHIRString {
1879 value: Some("Alice".to_string()),
1880 ..Default::default()
1881 })]),
1882 ..Default::default()
1883 })]),
1884 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1885 value: Some(true),
1886 ..Default::default()
1887 }))),
1888 ..Default::default()
1889 };
1890
1891 let result = engine
1892 .evaluate("descendants().ofType(Reference)", vec![&patient])
1893 .await
1894 .unwrap();
1895
1896 assert_eq!(
1897 result
1898 .values
1899 .iter()
1900 .map(|v| v.fhir_type())
1901 .collect::<Vec<_>>(),
1902 vec!["Reference",]
1903 );
1904
1905 let value = result.values[0]
1906 .as_any()
1907 .downcast_ref::<Reference>()
1908 .unwrap();
1909
1910 assert_eq!(
1911 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
1912 "Patient/123"
1913 );
1914
1915 unsafe {
1918 let r = value as *const Reference;
1919 let mut_ptr = r as *mut Reference;
1920
1921 (*mut_ptr).reference = Some(Box::new(FHIRString {
1922 value: Some("Patient/456".to_string()),
1923 ..Default::default()
1924 }));
1925 }
1926
1927 assert_eq!(
1928 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
1929 "Patient/456"
1930 );
1931
1932 assert_eq!(
1933 patient.link.as_ref().unwrap()[0]
1934 .other
1935 .reference
1936 .as_ref()
1937 .unwrap()
1938 .value
1939 .as_ref()
1940 .unwrap(),
1941 "Patient/456"
1942 );
1943 }
1944
1945 #[tokio::test]
1946 async fn test_external_constant_function() {
1947 let engine = FPEngine::new();
1948
1949 let config = Arc::new(Config {
1950 variable_resolver: (Some(ExternalConstantResolver::Function(Box::new(|v| {
1951 Box::pin(async move {
1952 match v.as_ref() {
1953 "test_variable" => Some(ResolvedValue::Box(Box::new(Patient {
1954 name: Some(vec![Box::new(HumanName {
1955 given: Some(vec![Box::new(FHIRString {
1956 value: Some("Paul".to_string()),
1957 ..Default::default()
1958 })]),
1959 ..Default::default()
1960 })]),
1961 ..Default::default()
1962 })
1963 as Box<dyn MetaValue>)),
1964 _ => None,
1965 }
1966 })
1967 })))),
1968 });
1969
1970 let result = engine
1971 .evaluate_with_config("%test_variable.name.given", vec![], config)
1972 .await
1973 .unwrap();
1974
1975 let value = result.values[0]
1976 .as_any()
1977 .downcast_ref::<FHIRString>()
1978 .unwrap();
1979
1980 assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
1981 }
1982
1983 #[tokio::test]
1984 async fn test_external_constant_function_reference() {
1985 let engine = FPEngine::new();
1986
1987 let patient = Arc::new(Patient {
1988 name: Some(vec![Box::new(HumanName {
1989 given: Some(vec![Box::new(FHIRString {
1990 value: Some("Paul".to_string()),
1991 ..Default::default()
1992 })]),
1993 ..Default::default()
1994 })]),
1995 ..Default::default()
1996 });
1997
1998 let resolver = {
1999 let patient = patient.clone();
2000 ExternalConstantResolver::Function(Box::new(move |v| {
2001 let patient = patient.clone();
2002 Box::pin(async move {
2003 match v.as_ref() {
2005 "test_variable" => Some(ResolvedValue::Arc(patient.clone())),
2006 _ => None,
2007 }
2008 })
2009 }))
2010 };
2011
2012 let config = Arc::new(Config {
2013 variable_resolver: (Some(resolver)),
2014 });
2015
2016 let result = engine
2017 .evaluate_with_config("%test_variable.name.given", vec![], config)
2018 .await
2019 .unwrap();
2020
2021 let value = result.values[0]
2022 .as_any()
2023 .downcast_ref::<FHIRString>()
2024 .unwrap();
2025
2026 assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
2027 }
2028
2029 #[tokio::test]
2030 async fn test_upper_function() {
2031 let engine = FPEngine::new();
2032
2033 let result = engine.evaluate("'hello'.upper()", vec![]).await.unwrap();
2034 assert_eq!(result.values.len(), 1);
2035 let value = result.values[0]
2036 .as_any()
2037 .downcast_ref::<FHIRString>()
2038 .unwrap();
2039 assert_eq!(value.value.as_deref(), Some("HELLO"));
2040
2041 let result = engine.evaluate("'AbCd'.upper()", vec![]).await.unwrap();
2042 let value = result.values[0]
2043 .as_any()
2044 .downcast_ref::<FHIRString>()
2045 .unwrap();
2046 assert_eq!(value.value.as_deref(), Some("ABCD"));
2047
2048 let result = engine.evaluate("'XYZ'.upper()", vec![]).await.unwrap();
2049 let value = result.values[0]
2050 .as_any()
2051 .downcast_ref::<FHIRString>()
2052 .unwrap();
2053 assert_eq!(value.value.as_deref(), Some("XYZ"));
2054 }
2055
2056 #[tokio::test]
2057 async fn test_lower_function() {
2058 let engine = FPEngine::new();
2059
2060 let result = engine.evaluate("'HELLO'.lower()", vec![]).await.unwrap();
2061 assert_eq!(result.values.len(), 1);
2062 let value = result.values[0]
2063 .as_any()
2064 .downcast_ref::<FHIRString>()
2065 .unwrap();
2066 assert_eq!(value.value.as_deref(), Some("hello"));
2067
2068 let result = engine.evaluate("'AbCd'.lower()", vec![]).await.unwrap();
2069 let value = result.values[0]
2070 .as_any()
2071 .downcast_ref::<FHIRString>()
2072 .unwrap();
2073 assert_eq!(value.value.as_deref(), Some("abcd"));
2074
2075 let result = engine.evaluate("'xyz'.lower()", vec![]).await.unwrap();
2076 let value = result.values[0]
2077 .as_any()
2078 .downcast_ref::<FHIRString>()
2079 .unwrap();
2080 assert_eq!(value.value.as_deref(), Some("xyz"));
2081 }
2082}