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::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
25/// Number types to use in FHIR evaluation
26static 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
167/// Need special handling as the first term could start with a type filter.
168/// for example Patient.name
169async 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 one of operands is empty per spec return an empty collection
221    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        // Special handling for reference which is to check the reference type.
378        "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        // Faking resolve to just return current context.
420        "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                    // Empty set effectively means no match and treat as false.
439                } 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            // Descendants is shorthand for repeat(children()) see [https://hl7.org/fhirpath/N1/#descendants-collection].
538            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        // https://hl7.org/fhirpath/N1/#conversion for implicit conversion rules todo.
603        //
604        // If types do not match return false.
605        // Should consider implicit conversion rules here but for now
606        // FPs like 'Patient.deceased.exists() and Patient.deceased != false' (deceased is either boolean or dateTime)
607        // Should return false rather than error.
608        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
844/// Need a means to store objects that are created during evaluation.
845struct 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        // Should be safe to unwrap as value guaranteed to be non-empty from above call.
862        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    /// Evaluate a FHIRPath expression against a context.
974    /// The context is a vector of references to MetaValue objects.
975    /// The path is a FHIRPath expression.
976    /// The result is a vector of references to MetaValue objects.
977    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        // Store created.
988        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    /// Evaluate a FHIRPath expression against a context.
997    /// The context is a vector of references to MetaValue objects.
998    /// The path is a FHIRPath expression.
999    /// The result is a vector of references to MetaValue objects.
1000    ///
1001    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        // Store created.
1013        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) = &param.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        // String tests
1304        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        // Number tests
1328        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        // Boolean tests
1352        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        // Nested Equality tests
1376        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 --&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>"
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        // An example for use in transaction processing where we have a reference to an object
1892        // but need to modify it in place.
1893        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                    // let patient = patient.clone();
1980                    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}