Skip to main content

haste_fhirpath/
lib.rs

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