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