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::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 match value.typename() {
377 "Reference" => {
379 if type_to_check == "Reference" {
380 return true;
381 } else if let Some(reference) = value.as_any().downcast_ref::<Reference>() {
382 if let Some(resource_type) = reference
383 .reference
384 .as_ref()
385 .and_then(|r| r.value.as_ref())
386 .and_then(|r| r.split("/").next())
387 {
388 return check_type_name(resource_type, type_to_check);
389 }
390 }
391 false
392 }
393 _ => check_type_name(value.typename(), type_to_check),
394 }
395}
396
397fn filter_by_type<'a>(type_name: &str, context: &Context<'a>) -> Context<'a> {
398 context.new_context_from(
399 context
400 .values
401 .iter()
402 .filter(|v| check_type(**v, type_name))
403 .map(|v| *v)
404 .collect(),
405 )
406}
407
408#[derive(Debug, Reflect)]
409struct Reflection {
410 name: String,
411}
412
413async fn evaluate_function<'a>(
414 function: &FunctionInvocation,
415 context: Context<'a>,
416 config: Option<Arc<Config<'a>>>,
417) -> Result<Context<'a>, FHIRPathError> {
418 match function.name.0.as_str() {
419 "resolve" => Ok(context),
421 "where" => {
422 validate_arguments(&function.arguments, &Cardinality::One)?;
423
424 let where_condition = &function.arguments[0];
425 let mut new_context = vec![];
426 for value in context.values.iter() {
427 let result = evaluate_expression(
428 where_condition,
429 context.new_context_from(vec![*value]),
430 config.clone(),
431 )
432 .await?;
433
434 if result.values.len() > 1 {
435 return Err(FHIRPathError::InternalError(
436 "Where condition did not return a single value".to_string(),
437 ));
438 } else if !result.values.is_empty() && downcast_bool(result.values[0])? == true {
440 new_context.push(*value);
441 }
442 }
443 Ok(context.new_context_from(new_context))
444 }
445 "ofType" => {
446 validate_arguments(&function.arguments, &Cardinality::One)?;
447
448 let type_name = derive_typename(&function.arguments[0])?;
449 Ok(filter_by_type(&type_name, &context))
450 }
451 "count" => {
452 validate_arguments(&function.arguments, &Cardinality::Zero)?;
453
454 let count = context.values.len() as i64;
455 Ok(
456 context.new_context_from(vec![context.allocate(ResolvedValue::Box(Box::new(
457 FHIRInteger {
458 value: Some(count),
459 ..Default::default()
460 },
461 )))]),
462 )
463 }
464 "as" => {
465 validate_arguments(&function.arguments, &Cardinality::One)?;
466
467 let type_name = derive_typename(&function.arguments[0])?;
468 Ok(filter_by_type(&type_name, &context))
469 }
470 "empty" => {
471 validate_arguments(&function.arguments, &Cardinality::Zero)?;
472 let res = Ok(context.new_context_from(vec![
473 context.allocate(ResolvedValue::Box(Box::new(context.values.is_empty()))),
474 ]));
475
476 res
477 }
478 "exists" => {
479 validate_arguments(&function.arguments, &Cardinality::Many)?;
480
481 if function.arguments.len() > 1 {
482 return Err(FunctionError::InvalidCardinality(
483 "exists".to_string(),
484 function.arguments.len(),
485 )
486 .into());
487 }
488
489 let context = if function.arguments.len() == 1 {
490 evaluate_expression(&function.arguments[0], context, config).await?
491 } else {
492 context
493 };
494
495 let res = Ok(context.new_context_from(vec![
496 context.allocate(ResolvedValue::Box(Box::new(!context.values.is_empty()))),
497 ]));
498
499 res
500 }
501 "children" => {
502 validate_arguments(&function.arguments, &Cardinality::Zero)?;
503
504 Ok(context.new_context_from(
505 context
506 .values
507 .iter()
508 .flat_map(|value| {
509 let result = value
510 .fields()
511 .iter()
512 .filter_map(|f| value.get_field(f).map(|v| v.flatten()))
513 .flatten()
514 .collect::<Vec<_>>();
515 result
516 })
517 .collect(),
518 ))
519 }
520 "repeat" => {
521 validate_arguments(&function.arguments, &Cardinality::One)?;
522
523 let projection = &function.arguments[0];
524 let mut end_result = vec![];
525 let mut cur = context;
526
527 while cur.values.len() != 0 {
528 cur = evaluate_expression(projection, cur, config.clone()).await?;
529 end_result.extend_from_slice(cur.values.as_slice());
530 }
531
532 Ok(cur.new_context_from(end_result))
533 }
534 "descendants" => {
535 validate_arguments(&function.arguments, &Cardinality::Zero)?;
536
537 let result = evaluate_expression(
539 &Expression::Singular(vec![Term::Invocation(Invocation::Function(
540 FunctionInvocation {
541 name: Identifier("repeat".to_string()),
542 arguments: vec![Expression::Singular(vec![Term::Invocation(
543 Invocation::Function(FunctionInvocation {
544 name: Identifier("children".to_string()),
545 arguments: vec![],
546 }),
547 )])],
548 },
549 ))]),
550 context,
551 config,
552 )
553 .await?;
554
555 Ok(result)
556 }
557 "type" => {
558 validate_arguments(&function.arguments, &Cardinality::Zero)?;
559
560 Ok(context.new_context_from(
561 context
562 .values
563 .iter()
564 .map(|value| {
565 let type_name = value.typename();
566 context.allocate(ResolvedValue::Box(Box::new(Reflection {
567 name: type_name.to_string(),
568 })))
569 })
570 .collect(),
571 ))
572 }
573 _ => {
574 return Err(FHIRPathError::NotImplemented(format!(
575 "Function '{}' is not implemented",
576 function.name.0
577 )));
578 }
579 }
580}
581
582fn equal_check<'b>(left: &Context<'b>, right: &Context<'b>) -> Result<bool, FHIRPathError> {
583 if NUMBER_TYPES.contains(left.values[0].typename())
584 && NUMBER_TYPES.contains(right.values[0].typename())
585 {
586 let left_value = downcast_number(left.values[0])?;
587 let right_value = downcast_number(right.values[0])?;
588 Ok(left_value == right_value)
589 } else if STRING_TYPES.contains(left.values[0].typename())
590 && STRING_TYPES.contains(right.values[0].typename())
591 {
592 let left_value = downcast_string(left.values[0])?;
593 let right_value = downcast_string(right.values[0])?;
594 Ok(left_value == right_value)
595 } else if BOOLEAN_TYPES.contains(left.values[0].typename())
596 && BOOLEAN_TYPES.contains(right.values[0].typename())
597 {
598 let left_value = downcast_bool(left.values[0])?;
599 let right_value = downcast_bool(right.values[0])?;
600 Ok(left_value == right_value)
601 } else {
602 Ok(false)
609 }
610}
611
612async fn evaluate_operation<'a>(
613 operation: &Operation,
614 context: Context<'a>,
615 config: Option<Arc<Config<'a>>>,
616) -> Result<Context<'a>, FHIRPathError> {
617 match operation {
618 Operation::Add(left, right) => {
619 operation_1(left, right, context, config, |left, right| {
620 if NUMBER_TYPES.contains(left.values[0].typename())
621 && NUMBER_TYPES.contains(right.values[0].typename())
622 {
623 let left_value = downcast_number(left.values[0])?;
624 let right_value = downcast_number(right.values[0])?;
625 Ok(left.new_context_from(vec![
626 left.allocate(ResolvedValue::Box(Box::new(left_value + right_value))),
627 ]))
628 } else if STRING_TYPES.contains(left.values[0].typename())
629 && STRING_TYPES.contains(right.values[0].typename())
630 {
631 let left_string = downcast_string(left.values[0])?;
632 let right_string = downcast_string(right.values[0])?;
633
634 Ok(left.new_context_from(vec![
635 left.allocate(ResolvedValue::Box(Box::new(left_string + &right_string))),
636 ]))
637 } else {
638 Err(FHIRPathError::OperationError(OperationError::TypeMismatch(
639 left.values[0].typename(),
640 right.values[0].typename(),
641 )))
642 }
643 })
644 .await
645 }
646 Operation::Subtraction(left, right) => {
647 operation_1(left, right, context, config, |left, right| {
648 let left_value = downcast_number(left.values[0])?;
649 let right_value = downcast_number(right.values[0])?;
650
651 Ok(left.new_context_from(vec![
652 left.allocate(ResolvedValue::Box(Box::new(left_value - right_value))),
653 ]))
654 })
655 .await
656 }
657 Operation::Multiplication(left, right) => {
658 operation_1(left, right, context, config, |left, right| {
659 let left_value = downcast_number(left.values[0])?;
660 let right_value = downcast_number(right.values[0])?;
661
662 Ok(left.new_context_from(vec![
663 left.allocate(ResolvedValue::Box(Box::new(left_value * right_value))),
664 ]))
665 })
666 .await
667 }
668 Operation::Division(left, right) => {
669 operation_1(left, right, context, config, |left, right| {
670 let left_value = downcast_number(left.values[0])?;
671 let right_value = downcast_number(right.values[0])?;
672
673 Ok(left.new_context_from(vec![
674 left.allocate(ResolvedValue::Box(Box::new(left_value / right_value))),
675 ]))
676 })
677 .await
678 }
679 Operation::Equal(left, right) => {
680 operation_1(left, right, context, config, |left, right| {
681 let are_equal = FHIRBoolean {
682 value: Some(equal_check(&left, &right)?),
683 ..Default::default()
684 };
685 Ok(left
686 .new_context_from(vec![left.allocate(ResolvedValue::Box(Box::new(are_equal)))]))
687 })
688 .await
689 }
690 Operation::NotEqual(left, right) => {
691 operation_1(left, right, context, config, |left, right| {
692 let not_equal = FHIRBoolean {
693 value: Some(!equal_check(&left, &right)?),
694 ..Default::default()
695 };
696 Ok(left
697 .new_context_from(vec![left.allocate(ResolvedValue::Box(Box::new(not_equal)))]))
698 })
699 .await
700 }
701 Operation::And(left, right) => {
702 operation_1(left, right, context, config, |left, right| {
703 let left_value = downcast_bool(left.values[0])?;
704 let right_value = downcast_bool(right.values[0])?;
705
706 Ok(
707 left.new_context_from(vec![left.allocate(ResolvedValue::Box(Box::new(
708 FHIRBoolean {
709 value: Some(left_value && right_value),
710 ..Default::default()
711 },
712 )))]),
713 )
714 })
715 .await
716 }
717 Operation::Or(left, right) => {
718 operation_1(left, right, context, config, |left, right| {
719 let left_value = downcast_bool(left.values[0])?;
720 let right_value = downcast_bool(right.values[0])?;
721
722 Ok(
723 left.new_context_from(vec![left.allocate(ResolvedValue::Box(Box::new(
724 FHIRBoolean {
725 value: Some(left_value || right_value),
726 ..Default::default()
727 },
728 )))]),
729 )
730 })
731 .await
732 }
733 Operation::Union(left, right) => {
734 operation_n(left, right, context, config, |left, right| {
735 let mut union = vec![];
736 union.extend(left.values.iter());
737 union.extend(right.values.iter());
738 Ok(left.new_context_from(union))
739 })
740 .await
741 }
742 Operation::Modulo(_, _) => Err(FHIRPathError::NotImplemented("Modulo".to_string())),
743 Operation::Is(expression, type_name) => {
744 let left = evaluate_expression(expression, context, config).await?;
745 if left.values.len() > 1 {
746 Err(FHIRPathError::OperationError(
747 OperationError::InvalidCardinality,
748 ))
749 } else {
750 if let Some(type_name) = type_name.0.get(0).as_ref().map(|k| &k.0) {
751 let next_context = filter_by_type(&type_name, &left);
752 Ok(
753 left.new_context_from(vec![left.allocate(ResolvedValue::Box(Box::new(
754 !next_context.values.is_empty(),
755 )))]),
756 )
757 } else {
758 Ok(left.new_context_from(vec![]))
759 }
760 }
761 }
762 Operation::As(expression, type_name) => {
763 let left = evaluate_expression(expression, context, config).await?;
764 if left.values.len() > 1 {
765 Err(FHIRPathError::OperationError(
766 OperationError::InvalidCardinality,
767 ))
768 } else {
769 if let Some(type_name) = type_name.0.get(0).as_ref().map(|k| &k.0) {
770 Ok(filter_by_type(&type_name, &left))
771 } else {
772 Ok(left.new_context_from(vec![]))
773 }
774 }
775 }
776 Operation::Polarity(_, _) => Err(FHIRPathError::NotImplemented("Polarity".to_string())),
777 Operation::DivisionTruncated(_, _) => Err(FHIRPathError::NotImplemented(
778 "DivisionTruncated".to_string(),
779 )),
780 Operation::LessThan(_, _) => Err(FHIRPathError::NotImplemented("LessThan".to_string())),
781 Operation::GreaterThan(_, _) => {
782 Err(FHIRPathError::NotImplemented("GreaterThan".to_string()))
783 }
784 Operation::LessThanEqual(_, _) => {
785 Err(FHIRPathError::NotImplemented("LessThanEqual".to_string()))
786 }
787 Operation::GreaterThanEqual(_, _) => Err(FHIRPathError::NotImplemented(
788 "GreaterThanEqual".to_string(),
789 )),
790 Operation::Equivalent(_, _) => Err(FHIRPathError::NotImplemented("Equivalent".to_string())),
791
792 Operation::NotEquivalent(_, _) => {
793 Err(FHIRPathError::NotImplemented("NotEquivalent".to_string()))
794 }
795 Operation::In(_left, _right) => Err(FHIRPathError::NotImplemented("In".to_string())),
796 Operation::Contains(_left, _right) => {
797 Err(FHIRPathError::NotImplemented("Contains".to_string()))
798 }
799 Operation::XOr(left, right) => {
800 operation_1(left, right, context, config, |left, right| {
801 let left_value = downcast_bool(left.values[0])?;
802 let right_value = downcast_bool(right.values[0])?;
803
804 Ok(
805 left.new_context_from(vec![left.allocate(ResolvedValue::Box(Box::new(
806 FHIRBoolean {
807 value: Some(left_value ^ right_value),
808 ..Default::default()
809 },
810 )))]),
811 )
812 })
813 .await
814 }
815 Operation::Implies(_left, _right) => {
816 Err(FHIRPathError::NotImplemented("Implies".to_string()))
817 }
818 }
819}
820
821fn evaluate_expression<'a>(
822 ast: &Expression,
823 context: Context<'a>,
824 config: Option<Arc<Config<'a>>>,
825) -> Pin<Box<impl Future<Output = Result<Context<'a>, FHIRPathError>>>> {
826 Box::pin(async move {
827 match ast {
828 Expression::Operation(operation) => {
829 evaluate_operation(operation, context, config).await
830 }
831 Expression::Singular(singular_ast) => {
832 evaluate_singular(singular_ast, context, config).await
833 }
834 }
835 })
836}
837
838#[derive(Debug)]
839pub enum ResolvedValue {
840 Box(Box<dyn MetaValue>),
841 Arc(Arc<dyn MetaValue>),
842}
843
844struct Allocator<'a> {
846 pub context: Vec<ResolvedValue>,
847 _marker: PhantomData<&'a dyn MetaValue>,
848}
849
850impl<'a> Allocator<'a> {
851 pub fn new() -> Self {
852 Allocator {
853 context: Vec::new(),
854 _marker: PhantomData,
855 }
856 }
857
858 pub fn allocate(&mut self, value: ResolvedValue) -> &'a dyn MetaValue {
859 self.context.push(value);
860
861 let ptr = match &self.context.last().unwrap() {
863 ResolvedValue::Box(b) => &**b as *const _,
864 ResolvedValue::Arc(a) => &**a,
865 };
866
867 unsafe { &*ptr }
868 }
869}
870
871pub struct Context<'a> {
872 allocator: Arc<Mutex<Allocator<'a>>>,
873 values: Arc<Vec<&'a dyn MetaValue>>,
874}
875
876pub enum ExternalConstantResolver<'a> {
877 Function(
878 Box<
879 dyn Fn(String) -> Pin<Box<dyn Future<Output = Option<ResolvedValue>> + Send>>
880 + Send
881 + Sync,
882 >,
883 ),
884 Variable(HashMap<String, &'a dyn MetaValue>),
885}
886
887pub struct Config<'a> {
888 pub variable_resolver: Option<ExternalConstantResolver<'a>>,
889}
890
891async fn resolve_external_constant<'a>(
892 name: &str,
893 resolver: Option<&ExternalConstantResolver<'a>>,
894 context: Context<'a>,
895) -> Result<Context<'a>, FHIRPathError> {
896 let external_constant = match resolver {
897 Some(ExternalConstantResolver::Function(func)) => {
898 let result = func(name.to_string()).await;
899
900 if let Some(result) = result {
901 Some(context.allocate(result))
902 } else {
903 None
904 }
905 }
906 Some(ExternalConstantResolver::Variable(map)) => map.get(name).map(|s| *s),
907 None => None,
908 };
909
910 if let Some(result) = external_constant {
911 return Ok(context.new_context_from(vec![result]));
912 } else {
913 return Ok(context.new_context_from(vec![]));
914 }
915}
916
917impl<'a> Context<'a> {
918 fn new(values: Vec<&'a dyn MetaValue>, allocator: Arc<Mutex<Allocator<'a>>>) -> Self {
919 Self {
920 allocator,
921 values: Arc::new(values),
922 }
923 }
924 fn new_context_from(&self, values: Vec<&'a dyn MetaValue>) -> Self {
925 Self {
926 allocator: self.allocator.clone(),
927 values: Arc::new(values),
928 }
929 }
930 fn allocate(&self, value: ResolvedValue) -> &'a dyn MetaValue {
931 self.allocator.lock().unwrap().allocate(value)
932 }
933 pub fn iter(&'a self) -> Box<dyn Iterator<Item = &'a dyn MetaValue> + 'a> {
934 Box::new(self.values.iter().map(|v| *v))
935 }
936}
937
938impl Clone for Context<'_> {
939 fn clone(&self) -> Self {
940 Self {
941 allocator: self.allocator.clone(),
942 values: self.values.clone(),
943 }
944 }
945}
946
947pub struct FPEngine {}
948
949static AST: LazyLock<Arc<DashMap<String, Expression>>> = LazyLock::new(|| Arc::new(DashMap::new()));
950
951fn get_ast(
952 path: &str,
953) -> Result<dashmap::mapref::one::Ref<'static, String, Expression>, FHIRPathError> {
954 let ast: dashmap::mapref::one::Ref<'_, String, Expression> =
955 if let Some(expression_ast) = AST.get(path) {
956 expression_ast
957 } else {
958 AST.insert(path.to_string(), parser::parse(path)?);
959 let expression_ast = AST.get(path).ok_or_else(|| {
960 FHIRPathError::InternalError("Failed to find path post insert".to_string())
961 })?;
962 expression_ast
963 };
964
965 Ok(ast)
966}
967
968impl FPEngine {
969 pub fn new() -> Self {
970 Self {}
971 }
972
973 pub async fn evaluate<'a, 'b>(
978 &self,
979 path: &str,
980 values: Vec<&'a dyn MetaValue>,
981 ) -> Result<Context<'b>, FHIRPathError>
982 where
983 'a: 'b,
984 {
985 let ast = get_ast(path)?;
986
987 let allocator: Arc<Mutex<Allocator<'b>>> = Arc::new(Mutex::new(Allocator::new()));
989
990 let context = Context::new(values, allocator.clone());
991
992 let result = evaluate_expression(&ast, context, None).await?;
993 Ok(result)
994 }
995
996 pub async fn evaluate_with_config<'a, 'b>(
1002 &self,
1003 path: &str,
1004 values: Vec<&'a dyn MetaValue>,
1005 config: Arc<Config<'b>>,
1006 ) -> Result<Context<'b>, FHIRPathError>
1007 where
1008 'a: 'b,
1009 {
1010 let ast = get_ast(path)?;
1011
1012 let allocator: Arc<Mutex<Allocator<'b>>> = Arc::new(Mutex::new(Allocator::new()));
1014
1015 let context = Context::new(values, allocator.clone());
1016
1017 let result = evaluate_expression(&ast, context, Some(config)).await?;
1018
1019 Ok(result)
1020 }
1021}
1022
1023#[cfg(test)]
1024mod tests {
1025 use super::*;
1026 use haste_fhir_model::r4::generated::{
1027 resources::{
1028 Bundle, Patient, PatientDeceasedTypeChoice, PatientLink, Resource, SearchParameter,
1029 },
1030 types::{
1031 Extension, ExtensionValueTypeChoice, FHIRString, FHIRUri, HumanName, Identifier,
1032 Reference,
1033 },
1034 };
1035 use haste_fhir_serialization_json;
1036 use haste_reflect_derive::Reflect;
1037
1038 #[derive(Reflect, Debug)]
1039 struct C {
1040 c: String,
1041 }
1042
1043 #[derive(Reflect, Debug)]
1044 struct B {
1045 b: Vec<Box<C>>,
1046 }
1047
1048 #[derive(Reflect, Debug)]
1049 struct A {
1050 a: Vec<Box<B>>,
1051 }
1052
1053 fn load_search_parameters() -> Vec<SearchParameter> {
1054 let json =
1055 include_str!("../../artifacts/artifacts/r4/hl7/minified/search-parameters.min.json");
1056 let bundle = haste_fhir_serialization_json::from_str::<Bundle>(json).unwrap();
1057
1058 let search_parameters: Vec<SearchParameter> = bundle
1059 .entry
1060 .unwrap_or_else(|| Vec::new())
1061 .into_iter()
1062 .map(|e| e.resource)
1063 .filter(|e| e.is_some())
1064 .filter_map(|e| match e {
1065 Some(k) => match *k {
1066 Resource::SearchParameter(sp) => Some(sp),
1067 _ => None,
1068 },
1069 _ => None,
1070 })
1071 .collect();
1072
1073 search_parameters
1074 }
1075
1076 #[tokio::test]
1077 async fn test_variable_resolution() {
1078 let engine = FPEngine::new();
1079 let patient = Patient {
1080 id: Some("my-patient".to_string()),
1081 ..Default::default()
1082 };
1083 let config = Arc::new(Config {
1084 variable_resolver: Some(ExternalConstantResolver::Variable(
1085 vec![("patient".to_string(), &patient as &dyn MetaValue)]
1086 .into_iter()
1087 .collect(),
1088 )),
1089 });
1090
1091 let result = engine
1092 .evaluate_with_config("%patient", vec![], config.clone())
1093 .await
1094 .unwrap();
1095
1096 assert_eq!(result.values.len(), 1);
1097 let p = result.values[0].as_any().downcast_ref::<Patient>().unwrap();
1098
1099 assert_eq!(p.id, patient.id);
1100
1101 let result_failed = engine
1102 .evaluate_with_config("%nobody", vec![], config)
1103 .await
1104 .unwrap();
1105
1106 assert_eq!(result_failed.values.len(), 0);
1107 }
1108
1109 #[tokio::test]
1110 async fn test_where_clause() {
1111 let engine = FPEngine::new();
1112 let mut patient = Patient::default();
1113 let mut identifier = Identifier::default();
1114 let extension = Extension {
1115 id: None,
1116 url: "test-extension".to_string(),
1117 extension: None,
1118 value: Some(ExtensionValueTypeChoice::String(Box::new(FHIRString {
1119 id: None,
1120 extension: None,
1121 value: Some("example value".to_string()),
1122 }))),
1123 };
1124 identifier.value = Some(Box::new(FHIRString {
1125 id: None,
1126 extension: Some(vec![Box::new(extension)]),
1127 value: Some("12345".to_string()),
1128 }));
1129 patient.identifier_ = Some(vec![Box::new(identifier)]);
1130
1131 let context = engine
1132 .evaluate(
1133 "$this.identifier.value.where($this.extension.value.exists())",
1134 vec![&patient],
1135 )
1136 .await;
1137
1138 assert_eq!(context.unwrap().values.len(), 1);
1139
1140 let context = engine
1141 .evaluate(
1142 "$this.identifier.value.where($this.extension.extension.exists())",
1143 vec![&patient],
1144 )
1145 .await;
1146 assert_eq!(context.unwrap().values.len(), 0);
1147 }
1148
1149 #[tokio::test]
1150 async fn test_all_parameters() {
1151 let search_parameters = load_search_parameters();
1152 for param in search_parameters.iter() {
1153 if let Some(expression) = ¶m.expression {
1154 let engine = FPEngine::new();
1155 let context = engine
1156 .evaluate(expression.value.as_ref().unwrap().as_str(), vec![])
1157 .await;
1158
1159 if let Err(err) = context {
1160 panic!(
1161 "Failed to evaluate search parameter '{}': {}",
1162 expression.value.as_ref().unwrap(),
1163 err
1164 );
1165 }
1166 }
1167 }
1168 }
1169
1170 fn test_patient() -> Patient {
1171 let mut patient = Patient::default();
1172 let mut name = HumanName::default();
1173 name.given = Some(vec![Box::new(FHIRString {
1174 id: None,
1175 extension: None,
1176 value: Some("Bob".to_string()),
1177 })]);
1178
1179 let mut mrn_identifier = Identifier::default();
1180 mrn_identifier.value = Some(Box::new(FHIRString {
1181 id: None,
1182 extension: None,
1183 value: Some("mrn-12345".to_string()),
1184 }));
1185 mrn_identifier.system = Some(Box::new(FHIRUri {
1186 id: None,
1187 extension: None,
1188 value: Some("mrn".to_string()),
1189 }));
1190
1191 let mut ssn_identifier = Identifier::default();
1192 ssn_identifier.value = Some(Box::new(FHIRString {
1193 id: None,
1194 extension: None,
1195 value: Some("ssn-12345".to_string()),
1196 }));
1197 ssn_identifier.system = Some(Box::new(FHIRUri {
1198 id: None,
1199 extension: None,
1200 value: Some("ssn".to_string()),
1201 }));
1202
1203 mrn_identifier.system = Some(Box::new(FHIRUri {
1204 id: None,
1205 extension: None,
1206 value: Some("mrn".to_string()),
1207 }));
1208
1209 patient.identifier_ = Some(vec![Box::new(mrn_identifier), Box::new(ssn_identifier)]);
1210 patient.name = Some(vec![Box::new(name)]);
1211 patient
1212 }
1213
1214 #[tokio::test]
1215 async fn indexing_tests() {
1216 let engine = FPEngine::new();
1217 let patient = test_patient();
1218
1219 let given_name = engine
1220 .evaluate("$this.name.given[0]", vec![&patient])
1221 .await
1222 .unwrap();
1223
1224 assert_eq!(given_name.values.len(), 1);
1225 let value = given_name.values[0];
1226 let name: &FHIRString = value
1227 .as_any()
1228 .downcast_ref::<FHIRString>()
1229 .expect("Failed to downcast to FHIRString");
1230
1231 assert_eq!(name.value.as_deref(), Some("Bob"));
1232
1233 let ssn_identifier = engine
1234 .evaluate("$this.identifier[1]", vec![&patient])
1235 .await
1236 .unwrap();
1237
1238 assert_eq!(ssn_identifier.values.len(), 1);
1239 let value = ssn_identifier.values[0];
1240 let identifier: &Identifier = value
1241 .as_any()
1242 .downcast_ref::<Identifier>()
1243 .expect("Failed to downcast to Identifier");
1244
1245 assert_eq!(
1246 identifier.value.as_ref().unwrap().value.as_deref(),
1247 Some("ssn-12345")
1248 );
1249
1250 let all_identifiers = engine
1251 .evaluate("$this.identifier", vec![&patient])
1252 .await
1253 .unwrap();
1254 assert_eq!(all_identifiers.values.len(), 2);
1255 }
1256
1257 #[tokio::test]
1258 async fn where_testing() {
1259 let engine = FPEngine::new();
1260 let patient = test_patient();
1261
1262 let name_where_clause = engine
1263 .evaluate(
1264 "$this.name.given.where($this.value = 'Bob')",
1265 vec![&patient],
1266 )
1267 .await
1268 .unwrap();
1269
1270 assert_eq!(name_where_clause.values.len(), 1);
1271 let value = name_where_clause.values[0];
1272 let name: &FHIRString = value
1273 .as_any()
1274 .downcast_ref::<FHIRString>()
1275 .expect("Failed to downcast to FHIRString");
1276
1277 assert_eq!(name.value.as_deref(), Some("Bob"));
1278
1279 let ssn_identifier_clause = engine
1280 .evaluate(
1281 "$this.identifier.where($this.system.value = 'ssn')",
1282 vec![&patient],
1283 )
1284 .await
1285 .unwrap();
1286 assert_eq!(ssn_identifier_clause.values.len(), 1);
1287
1288 let ssn_identifier = ssn_identifier_clause.values[0]
1289 .as_any()
1290 .downcast_ref::<Identifier>()
1291 .expect("Failed to downcast to Identifier");
1292
1293 assert_eq!(
1294 ssn_identifier.value.as_ref().unwrap().value.as_deref(),
1295 Some("ssn-12345")
1296 );
1297 }
1298
1299 #[tokio::test]
1300 async fn test_equality() {
1301 let engine = FPEngine::new();
1302
1303 let string_equal = engine.evaluate("'test' = 'test'", vec![]).await.unwrap();
1305 for r in string_equal.iter() {
1306 let b: bool = r
1307 .as_any()
1308 .downcast_ref::<FHIRBoolean>()
1309 .unwrap()
1310 .value
1311 .unwrap()
1312 .clone();
1313 assert_eq!(b, true);
1314 }
1315 let string_unequal = engine.evaluate("'invalid' = 'test'", vec![]).await.unwrap();
1316 for r in string_unequal.iter() {
1317 let b: bool = r
1318 .as_any()
1319 .downcast_ref::<FHIRBoolean>()
1320 .unwrap()
1321 .value
1322 .unwrap()
1323 .clone();
1324 assert_eq!(b, false);
1325 }
1326
1327 let number_equal = engine.evaluate("12 = 12", vec![]).await.unwrap();
1329 for r in number_equal.iter() {
1330 let b: bool = r
1331 .as_any()
1332 .downcast_ref::<FHIRBoolean>()
1333 .unwrap()
1334 .value
1335 .unwrap()
1336 .clone();
1337 assert_eq!(b, true);
1338 }
1339 let number_unequal = engine.evaluate("13 = 12", vec![]).await.unwrap();
1340 for r in number_unequal.iter() {
1341 let b: bool = r
1342 .as_any()
1343 .downcast_ref::<FHIRBoolean>()
1344 .unwrap()
1345 .value
1346 .unwrap()
1347 .clone();
1348 assert_eq!(b, false);
1349 }
1350
1351 let bool_equal = engine.evaluate("false = false", vec![]).await.unwrap();
1353 for r in bool_equal.iter() {
1354 let b: bool = r
1355 .as_any()
1356 .downcast_ref::<FHIRBoolean>()
1357 .unwrap()
1358 .value
1359 .unwrap()
1360 .clone();
1361 assert_eq!(b, true);
1362 }
1363 let bool_unequal = engine.evaluate("false = true", vec![]).await.unwrap();
1364 for r in bool_unequal.iter() {
1365 let b: bool = r
1366 .as_any()
1367 .downcast_ref::<FHIRBoolean>()
1368 .unwrap()
1369 .value
1370 .unwrap()
1371 .clone();
1372 assert_eq!(b, false);
1373 }
1374
1375 let bool_equal = engine.evaluate("12 = 13 = false", vec![]).await.unwrap();
1377 for r in bool_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 bool_unequal = engine.evaluate("12 = 13 = true", vec![]).await.unwrap();
1388 for r in bool_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 let bool_unequal = engine.evaluate("12 = (13 - 1)", vec![]).await.unwrap();
1399 for r in bool_unequal.iter() {
1400 let b: bool = r
1401 .as_any()
1402 .downcast_ref::<FHIRBoolean>()
1403 .unwrap()
1404 .value
1405 .unwrap()
1406 .clone();
1407
1408 assert_eq!(b, true);
1409 }
1410 }
1411
1412 #[tokio::test]
1413 async fn test_string_concat() {
1414 let engine = FPEngine::new();
1415 let patient = test_patient();
1416
1417 let simple_result = engine.evaluate("'Hello' + ' World'", vec![]).await.unwrap();
1418 for r in simple_result.iter() {
1419 let s: String = r.as_any().downcast_ref::<String>().unwrap().clone();
1420 assert_eq!(s, "Hello World".to_string());
1421 }
1422
1423 let simple_result = engine
1424 .evaluate("$this.name.given + ' Miller'", vec![&patient])
1425 .await
1426 .unwrap();
1427 for r in simple_result.iter() {
1428 let s: String = r.as_any().downcast_ref::<String>().unwrap().clone();
1429 assert_eq!(s, "Bob Miller".to_string());
1430 }
1431 }
1432
1433 #[tokio::test]
1434 async fn test_simple() {
1435 let root = A {
1436 a: vec![Box::new(B {
1437 b: vec![Box::new(C {
1438 c: "whatever".to_string(),
1439 })],
1440 })],
1441 };
1442
1443 let engine = FPEngine::new();
1444 let result = engine.evaluate("a.b.c", vec![&root]).await.unwrap();
1445
1446 let strings: Vec<&String> = result
1447 .iter()
1448 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1449 .collect();
1450
1451 assert_eq!(strings, vec!["whatever"]);
1452 }
1453
1454 #[tokio::test]
1455 async fn allocation() {
1456 let engine = FPEngine::new();
1457 let result = engine.evaluate("'asdf'", vec![]).await.unwrap();
1458
1459 for r in result.iter() {
1460 let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1461
1462 assert_eq!(s.value, Some("asdf".to_string()));
1463 }
1464 }
1465
1466 #[tokio::test]
1467 async fn order_operation() {
1468 let engine = FPEngine::new();
1469 let result = engine.evaluate("45 + 2 * 3", vec![]).await.unwrap();
1470
1471 for r in result.iter() {
1472 let s = r.as_any().downcast_ref::<f64>().unwrap().clone();
1473
1474 assert_eq!(s, 51.0);
1475 }
1476 }
1477
1478 #[tokio::test]
1479 async fn xor_operation() {
1480 let engine = FPEngine::new();
1481 let result = engine.evaluate("true xor true", vec![]).await.unwrap();
1482
1483 for r in result.iter() {
1484 let b: bool = r
1485 .as_any()
1486 .downcast_ref::<FHIRBoolean>()
1487 .unwrap()
1488 .value
1489 .unwrap()
1490 .clone();
1491
1492 assert_eq!(b, false);
1493 }
1494 }
1495
1496 #[tokio::test]
1497 async fn domain_resource_filter() {
1498 let engine = FPEngine::new();
1499
1500 let patient = haste_fhir_serialization_json::from_str::<Resource>(
1501 r#"{"id": "patient-id", "resourceType": "Patient"}"#,
1502 )
1503 .unwrap();
1504 let result = engine
1505 .evaluate("Resource.id", vec![&patient])
1506 .await
1507 .unwrap();
1508 let ids: Vec<&String> = result
1509 .iter()
1510 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1511 .collect();
1512
1513 assert_eq!(ids.len(), 1);
1514 assert_eq!(ids[0], "patient-id");
1515
1516 let result2 = engine
1517 .evaluate("DomainResource.id", vec![&patient])
1518 .await
1519 .unwrap();
1520 let ids2: Vec<&String> = result2
1521 .iter()
1522 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1523 .collect();
1524 assert_eq!(ids2.len(), 1);
1525 assert_eq!(ids2[0], "patient-id");
1526 }
1527
1528 #[tokio::test]
1529 async fn type_test() {
1530 let engine = FPEngine::new();
1531 let patient = Patient::default();
1532
1533 let result = engine
1534 .evaluate("$this.type().name", vec![&patient])
1535 .await
1536 .unwrap();
1537 let ids: Vec<&String> = result
1538 .iter()
1539 .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1540 .collect();
1541
1542 assert_eq!(ids.len(), 1);
1543 assert_eq!(ids[0], "Patient");
1544 }
1545
1546 #[tokio::test]
1547 async fn resolve_test() {
1548 let engine = FPEngine::new();
1549 let observation = haste_fhir_serialization_json::from_str::<Resource>(r#"
1550 {
1551 "resourceType": "Observation",
1552 "id": "f001",
1553 "text": {
1554 "status": "generated",
1555 "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>"
1556 },
1557 "identifier": [
1558 {
1559 "use": "official",
1560 "system": "http://www.bmc.nl/zorgportal/identifiers/observations",
1561 "value": "6323"
1562 }
1563 ],
1564 "status": "final",
1565 "code": {
1566 "coding": [
1567 {
1568 "system": "http://loinc.org",
1569 "code": "15074-8",
1570 "display": "Glucose [Moles/volume] in Blood"
1571 }
1572 ]
1573 },
1574 "subject": {
1575 "reference": "Patient/f001",
1576 "display": "P. van de Heuvel"
1577 },
1578 "effectivePeriod": {
1579 "start": "2013-04-02T09:30:10+01:00"
1580 },
1581 "issued": "2013-04-03T15:30:10+01:00",
1582 "performer": [
1583 {
1584 "reference": "Practitioner/f005",
1585 "display": "A. Langeveld"
1586 }
1587 ],
1588 "valueQuantity": {
1589 "value": 6.3,
1590 "unit": "mmol/l",
1591 "system": "http://unitsofmeasure.org",
1592 "code": "mmol/L"
1593 },
1594 "interpretation": [
1595 {
1596 "coding": [
1597 {
1598 "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
1599 "code": "H",
1600 "display": "High"
1601 }
1602 ]
1603 }
1604 ],
1605 "referenceRange": [
1606 {
1607 "low": {
1608 "value": 3.1,
1609 "unit": "mmol/l",
1610 "system": "http://unitsofmeasure.org",
1611 "code": "mmol/L"
1612 },
1613 "high": {
1614 "value": 6.2,
1615 "unit": "mmol/l",
1616 "system": "http://unitsofmeasure.org",
1617 "code": "mmol/L"
1618 }
1619 }
1620 ]
1621 }
1622 "#).unwrap();
1623
1624 let result = engine
1625 .evaluate(
1626 "Observation.subject.where(resolve() is Patient)",
1627 vec![&observation],
1628 )
1629 .await
1630 .unwrap();
1631
1632 let references: Vec<&Reference> = result
1633 .iter()
1634 .map(|r| r.as_any().downcast_ref::<Reference>().unwrap())
1635 .collect();
1636
1637 assert_eq!(references.len(), 1);
1638 assert_eq!(
1639 references[0].reference.as_ref().unwrap().value,
1640 Some("Patient/f001".to_string())
1641 );
1642 }
1643
1644 #[tokio::test]
1645 async fn children_test() {
1646 let engine = FPEngine::new();
1647 let patient = Patient {
1648 name: Some(vec![Box::new(HumanName {
1649 given: Some(vec![Box::new(FHIRString {
1650 value: Some("Alice".to_string()),
1651 ..Default::default()
1652 })]),
1653 ..Default::default()
1654 })]),
1655 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1656 value: Some(true),
1657 ..Default::default()
1658 }))),
1659 ..Default::default()
1660 };
1661
1662 let result = engine
1663 .evaluate("$this.children()", vec![&patient])
1664 .await
1665 .unwrap();
1666
1667 assert_eq!(result.values.len(), 2);
1668 assert_eq!(
1669 result
1670 .values
1671 .iter()
1672 .map(|v| v.typename())
1673 .collect::<Vec<_>>(),
1674 vec!["HumanName", "FHIRBoolean"]
1675 );
1676 }
1677
1678 #[tokio::test]
1679 async fn repeat_test() {
1680 let engine = FPEngine::new();
1681 let patient = Patient {
1682 name: Some(vec![Box::new(HumanName {
1683 given: Some(vec![Box::new(FHIRString {
1684 value: Some("Alice".to_string()),
1685 ..Default::default()
1686 })]),
1687 ..Default::default()
1688 })]),
1689 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1690 value: Some(true),
1691 ..Default::default()
1692 }))),
1693 ..Default::default()
1694 };
1695
1696 let result = engine
1697 .evaluate("$this.name.given", vec![&patient])
1698 .await
1699 .unwrap();
1700
1701 assert_eq!(result.values.len(), 1);
1702
1703 assert_eq!(result.values[0].typename(), "FHIRString");
1704
1705 let result = engine
1706 .evaluate("$this.repeat(children())", vec![&patient])
1707 .await
1708 .unwrap();
1709
1710 assert_eq!(
1711 result
1712 .values
1713 .iter()
1714 .map(|v| v.typename())
1715 .collect::<Vec<_>>(),
1716 vec![
1717 "HumanName",
1718 "FHIRBoolean",
1719 "FHIRString",
1720 "http://hl7.org/fhirpath/System.Boolean",
1721 "http://hl7.org/fhirpath/System.String"
1722 ]
1723 );
1724 }
1725 #[tokio::test]
1726 async fn descendants_test() {
1727 let engine = FPEngine::new();
1728 let patient = Patient {
1729 name: Some(vec![Box::new(HumanName {
1730 given: Some(vec![Box::new(FHIRString {
1731 value: Some("Alice".to_string()),
1732 ..Default::default()
1733 })]),
1734 ..Default::default()
1735 })]),
1736 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1737 value: Some(true),
1738 ..Default::default()
1739 }))),
1740 ..Default::default()
1741 };
1742 let result = engine
1743 .evaluate("descendants()", vec![&patient])
1744 .await
1745 .unwrap();
1746
1747 assert_eq!(
1748 result
1749 .values
1750 .iter()
1751 .map(|v| v.typename())
1752 .collect::<Vec<_>>(),
1753 vec![
1754 "HumanName",
1755 "FHIRBoolean",
1756 "FHIRString",
1757 "http://hl7.org/fhirpath/System.Boolean",
1758 "http://hl7.org/fhirpath/System.String"
1759 ]
1760 );
1761 }
1762
1763 #[tokio::test]
1764 async fn descendants_test_filter() {
1765 let engine = FPEngine::new();
1766 let patient = Patient {
1767 link: Some(vec![PatientLink {
1768 other: Box::new(Reference {
1769 reference: Some(Box::new(FHIRString {
1770 value: Some("Patient/123".to_string()),
1771 ..Default::default()
1772 })),
1773 ..Default::default()
1774 }),
1775 ..Default::default()
1776 }]),
1777 name: Some(vec![Box::new(HumanName {
1778 given: Some(vec![Box::new(FHIRString {
1779 value: Some("Alice".to_string()),
1780 ..Default::default()
1781 })]),
1782 ..Default::default()
1783 })]),
1784 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1785 value: Some(true),
1786 ..Default::default()
1787 }))),
1788 ..Default::default()
1789 };
1790 let result = engine
1791 .evaluate("descendants()", vec![&patient])
1792 .await
1793 .unwrap();
1794
1795 assert_eq!(
1796 result
1797 .values
1798 .iter()
1799 .map(|v| v.typename())
1800 .collect::<Vec<_>>(),
1801 vec![
1802 "HumanName",
1803 "FHIRBoolean",
1804 "PatientLink",
1805 "FHIRString",
1806 "http://hl7.org/fhirpath/System.Boolean",
1807 "Reference",
1808 "http://hl7.org/fhirpath/System.String",
1809 "FHIRString",
1810 "http://hl7.org/fhirpath/System.String"
1811 ]
1812 );
1813
1814 let result = engine
1815 .evaluate("descendants().ofType(Reference)", 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!["Reference",]
1826 );
1827
1828 let value = result.values[0]
1829 .as_any()
1830 .downcast_ref::<Reference>()
1831 .unwrap();
1832
1833 assert_eq!(
1834 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
1835 "Patient/123"
1836 );
1837 }
1838
1839 #[tokio::test]
1840 async fn try_unsafe_set_from_ref() {
1841 let engine = FPEngine::new();
1842 let patient = Patient {
1843 link: Some(vec![PatientLink {
1844 other: Box::new(Reference {
1845 reference: Some(Box::new(FHIRString {
1846 value: Some("Patient/123".to_string()),
1847 ..Default::default()
1848 })),
1849 ..Default::default()
1850 }),
1851 ..Default::default()
1852 }]),
1853 name: Some(vec![Box::new(HumanName {
1854 given: Some(vec![Box::new(FHIRString {
1855 value: Some("Alice".to_string()),
1856 ..Default::default()
1857 })]),
1858 ..Default::default()
1859 })]),
1860 deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1861 value: Some(true),
1862 ..Default::default()
1863 }))),
1864 ..Default::default()
1865 };
1866
1867 let result = engine
1868 .evaluate("descendants().ofType(Reference)", vec![&patient])
1869 .await
1870 .unwrap();
1871
1872 assert_eq!(
1873 result
1874 .values
1875 .iter()
1876 .map(|v| v.typename())
1877 .collect::<Vec<_>>(),
1878 vec!["Reference",]
1879 );
1880
1881 let value = result.values[0]
1882 .as_any()
1883 .downcast_ref::<Reference>()
1884 .unwrap();
1885
1886 assert_eq!(
1887 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
1888 "Patient/123"
1889 );
1890
1891 unsafe {
1894 let r = value as *const Reference;
1895 let mut_ptr = r as *mut Reference;
1896
1897 (*mut_ptr).reference = Some(Box::new(FHIRString {
1898 value: Some("Patient/456".to_string()),
1899 ..Default::default()
1900 }));
1901 }
1902
1903 assert_eq!(
1904 value.reference.as_ref().unwrap().value.as_ref().unwrap(),
1905 "Patient/456"
1906 );
1907
1908 assert_eq!(
1909 patient.link.as_ref().unwrap()[0]
1910 .other
1911 .reference
1912 .as_ref()
1913 .unwrap()
1914 .value
1915 .as_ref()
1916 .unwrap(),
1917 "Patient/456"
1918 );
1919 }
1920
1921 #[tokio::test]
1922 async fn test_external_constant_function() {
1923 let engine = FPEngine::new();
1924
1925 let config = Arc::new(Config {
1926 variable_resolver: (Some(ExternalConstantResolver::Function(Box::new(|v| {
1927 Box::pin(async move {
1928 match v.as_ref() {
1929 "test_variable" => Some(ResolvedValue::Box(Box::new(Patient {
1930 name: Some(vec![Box::new(HumanName {
1931 given: Some(vec![Box::new(FHIRString {
1932 value: Some("Paul".to_string()),
1933 ..Default::default()
1934 })]),
1935 ..Default::default()
1936 })]),
1937 ..Default::default()
1938 })
1939 as Box<dyn MetaValue>)),
1940 _ => None,
1941 }
1942 })
1943 })))),
1944 });
1945
1946 let result = engine
1947 .evaluate_with_config("%test_variable.name.given", vec![], config)
1948 .await
1949 .unwrap();
1950
1951 let value = result.values[0]
1952 .as_any()
1953 .downcast_ref::<FHIRString>()
1954 .unwrap();
1955
1956 assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
1957 }
1958
1959 #[tokio::test]
1960 async fn test_external_constant_function_reference() {
1961 let engine = FPEngine::new();
1962
1963 let patient = Arc::new(Patient {
1964 name: Some(vec![Box::new(HumanName {
1965 given: Some(vec![Box::new(FHIRString {
1966 value: Some("Paul".to_string()),
1967 ..Default::default()
1968 })]),
1969 ..Default::default()
1970 })]),
1971 ..Default::default()
1972 });
1973
1974 let resolver = {
1975 let patient = patient.clone();
1976 ExternalConstantResolver::Function(Box::new(move |v| {
1977 let patient = patient.clone();
1978 Box::pin(async move {
1979 match v.as_ref() {
1981 "test_variable" => Some(ResolvedValue::Arc(patient.clone())),
1982 _ => None,
1983 }
1984 })
1985 }))
1986 };
1987
1988 let config = Arc::new(Config {
1989 variable_resolver: (Some(resolver)),
1990 });
1991
1992 let result = engine
1993 .evaluate_with_config("%test_variable.name.given", vec![], config)
1994 .await
1995 .unwrap();
1996
1997 let value = result.values[0]
1998 .as_any()
1999 .downcast_ref::<FHIRString>()
2000 .unwrap();
2001
2002 assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
2003 }
2004}