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