Skip to main content

haste_fhirpath/
lib.rs

1mod error;
2mod parser;
3use crate::{
4    error::{FunctionError, OperationError},
5    parser::{
6        Expression, FunctionInvocation, Identifier, Invocation, Literal, Operation,
7        QualifiedIdentifier, Term,
8    },
9};
10use dashmap::DashMap;
11pub use error::FHIRPathError;
12use haste_fhir_model::r4::{
13    conversion::{
14        BOOLEAN_TYPES, NUMBER_TYPES, STRING_TYPES, downcast_bool, downcast_number, downcast_string,
15    },
16    generated::{
17        resources::ResourceType,
18        types::{FHIRBoolean, FHIRDecimal, FHIRId, FHIRInteger, FHIRString, Reference},
19    },
20};
21use haste_reflect::MetaValue;
22use haste_reflect_derive::Reflect;
23use std::pin::Pin;
24use std::{
25    collections::HashMap,
26    sync::{Arc, LazyLock},
27};
28use tokio::sync::Mutex;
29
30mod allocators;
31use allocators::AllocatorTrait;
32
33async fn evaluate_literal<'b>(
34    literal: &Literal,
35    context: Context<'b>,
36) -> Result<Context<'b>, FHIRPathError> {
37    match literal {
38        Literal::String(string) => Ok(context.new_context_from(vec![
39            context
40                .allocate_literal(FHIRString {
41                    value: Some(string.clone()),
42                    ..Default::default()
43                })
44                .await,
45        ])),
46        Literal::Integer(int) => Ok(context.new_context_from(vec![
47            context
48                .allocate_literal(FHIRInteger {
49                    value: Some(*int),
50                    ..Default::default()
51                })
52                .await,
53        ])),
54        Literal::Float(decimal) => Ok(context.new_context_from(vec![
55            context
56                .allocate_literal(FHIRDecimal {
57                    value: Some(*decimal),
58                    ..Default::default()
59                })
60                .await,
61        ])),
62        Literal::Boolean(bool) => Ok(context.new_context_from(vec![
63            context
64                .allocate_literal(FHIRBoolean {
65                    value: Some(*bool),
66                    ..Default::default()
67                })
68                .await,
69        ])),
70        Literal::Null => Ok(context.new_context_from(vec![])),
71        _ => Err(FHIRPathError::InvalidLiteral(literal.to_owned())),
72    }
73}
74
75async fn evaluate_invocation<'a>(
76    invocation: &Invocation,
77    context: Context<'a>,
78    config: Option<Arc<Config<'a>>>,
79) -> Result<Context<'a>, FHIRPathError> {
80    match invocation {
81        Invocation::This => Ok(context),
82        Invocation::Index(index_expression) => {
83            let index = evaluate_expression(index_expression, context.clone(), config).await?;
84            if index.values.len() != 1 {
85                return Err(FHIRPathError::OperationError(
86                    OperationError::InvalidCardinality,
87                ));
88            }
89
90            let float_index = downcast_number(index.values[0])?;
91
92            // Ensure the index is non-negative and has no fractional part
93            if float_index < 0.0 || float_index.fract() != 0.0 {
94                return Err(FHIRPathError::OperationError(OperationError::InvalidIndex));
95            }
96
97            // Suppress clippy warnings because the float is guaranteed to be a valid,
98            // non-negative integer index by the safety checks immediately above.
99            #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
100            let index: usize = (float_index as u64)
101                .try_into()
102                .map_err(|_| FHIRPathError::OperationError(OperationError::IndexOutOfBounds))?;
103
104            if let Some(value) = context.values.get(index) {
105                Ok(context.new_context_from(vec![*value]))
106            } else {
107                Ok(context.new_context_from(vec![]))
108            }
109        }
110        Invocation::IndexAccessor => Err(FHIRPathError::NotImplemented("index access".to_string())),
111        Invocation::Total => Err(FHIRPathError::NotImplemented("total".to_string())),
112        Invocation::Identifier(Identifier(id)) => Ok(context.new_context_from(
113            context
114                .values
115                .iter()
116                .flat_map(|v| v.get_field(id).map_or_else(Vec::new, |v| v.flatten()))
117                .collect(),
118        )),
119        Invocation::Function(function) => evaluate_function(function, context, config).await,
120    }
121}
122
123async fn evaluate_term<'a>(
124    term: &Term,
125    context: Context<'a>,
126    config: Option<Arc<Config<'a>>>,
127) -> Result<Context<'a>, FHIRPathError> {
128    match term {
129        Term::Literal(literal) => evaluate_literal(literal, context).await,
130        Term::ExternalConstant(constant) => {
131            resolve_external_constant(
132                constant,
133                config.as_ref().and_then(|c| c.variable_resolver.as_ref()),
134                context,
135            )
136            .await
137        }
138        Term::Parenthesized(expression) => evaluate_expression(expression, context, config).await,
139        Term::Invocation(invocation) => evaluate_invocation(invocation, context, config).await,
140    }
141}
142
143/// Need special handling as the first term could start with a type filter.
144/// for example Patient.name
145async fn evaluate_first_term<'a>(
146    term: &Term,
147    context: Context<'a>,
148    config: Option<Arc<Config<'a>>>,
149) -> Result<Context<'a>, FHIRPathError> {
150    match term {
151        Term::Invocation(invocation) => match invocation {
152            Invocation::Identifier(identifier) => {
153                let type_filter = filter_by_type(&identifier.0, &context);
154                if type_filter.values.is_empty() {
155                    evaluate_invocation(invocation, context, config).await
156                } else {
157                    Ok(type_filter)
158                }
159            }
160            _ => evaluate_invocation(invocation, context, config).await,
161        },
162        _ => evaluate_term(term, context, config).await,
163    }
164}
165
166async fn evaluate_singular<'a>(
167    expression: &[Term],
168    context: Context<'a>,
169    config: Option<Arc<Config<'a>>>,
170) -> Result<Context<'a>, FHIRPathError> {
171    let mut current_context = context;
172
173    let mut term_iterator = expression.iter();
174    let first_term = term_iterator.next();
175    if let Some(first_term) = first_term {
176        current_context = evaluate_first_term(first_term, current_context, config.clone()).await?;
177    }
178
179    for term in term_iterator {
180        current_context = evaluate_term(term, current_context, config.clone()).await?;
181    }
182
183    Ok(current_context)
184}
185
186async fn operation_2<'a>(
187    left: &Expression,
188    right: &Expression,
189    context: Context<'a>,
190    config: Option<Arc<Config<'a>>>,
191    executor: impl Fn(
192        Context<'a>,
193        Context<'a>,
194    )
195        -> Pin<Box<dyn Future<Output = Result<Context<'a>, FHIRPathError>> + Send + 'a>>,
196) -> Result<Context<'a>, FHIRPathError> {
197    let left = evaluate_expression(left, context.clone(), config.clone()).await?;
198    let right = evaluate_expression(right, context, config).await?;
199
200    // If one of operands is empty per spec return an empty collection
201    if left.values.is_empty() || right.values.is_empty() {
202        return Ok(left.new_context_from(vec![]));
203    }
204
205    if left.values.len() != 1 || right.values.len() != 1 {
206        return Err(FHIRPathError::OperationError(
207            OperationError::InvalidCardinality,
208        ));
209    }
210
211    executor(left, right).await
212}
213
214async fn operation_n<'a>(
215    left: &Expression,
216    right: &Expression,
217    context: Context<'a>,
218    config: Option<Arc<Config<'a>>>,
219    executor: impl Fn(Context<'a>, Context<'a>) -> Result<Context<'a>, FHIRPathError>,
220) -> Result<Context<'a>, FHIRPathError> {
221    let left = evaluate_expression(left, context.clone(), config.clone()).await?;
222    let right = evaluate_expression(right, context, config).await?;
223    executor(left, right)
224}
225
226enum Cardinality {
227    Zero,
228    One,
229    Many,
230    Custom(usize, usize),
231}
232
233fn validate_arguments(
234    ast_arguments: &[Expression],
235    cardinality: &Cardinality,
236) -> Result<(), FHIRPathError> {
237    match cardinality {
238        Cardinality::Zero => {
239            if !ast_arguments.is_empty() {
240                return Err(FHIRPathError::OperationError(
241                    OperationError::InvalidCardinality,
242                ));
243            }
244        }
245        Cardinality::Custom(min, max) => {
246            if ast_arguments.len() < *min || ast_arguments.len() > *max {
247                return Err(FHIRPathError::OperationError(
248                    OperationError::InvalidCardinality,
249                ));
250            }
251        }
252        Cardinality::One => {
253            if ast_arguments.len() != 1 {
254                return Err(FHIRPathError::OperationError(
255                    OperationError::InvalidCardinality,
256                ));
257            }
258        }
259        Cardinality::Many => {}
260    }
261    Ok(())
262}
263
264fn derive_typename(expression_ast: &Expression) -> Result<String, FHIRPathError> {
265    match expression_ast {
266        Expression::Singular(ast) => match &ast[0] {
267            Term::Invocation(Invocation::Identifier(type_id)) => Ok(type_id.0.clone()),
268            _ => Err(FHIRPathError::FailedTypeNameDerivation),
269        },
270        Expression::Operation(_) => Err(FHIRPathError::FailedTypeNameDerivation),
271    }
272}
273
274fn check_type_name(type_name: &str, type_to_check: &str) -> bool {
275    match type_to_check {
276        "Resource" | "DomainResource" => ResourceType::try_from(type_name).is_ok(),
277        _ => type_name == type_to_check,
278    }
279}
280
281fn check_type(value: &dyn MetaValue, type_to_check: &str) -> bool {
282    let fhir_type_name = value.fhir_type();
283
284    match fhir_type_name {
285        // Special handling for reference which is to check the reference type.
286        "Reference" => {
287            if type_to_check == "Reference" {
288                return true;
289            } else if let Some(reference) = value.as_any().downcast_ref::<Reference>()
290                && let Some(resource_type) = reference
291                    .reference
292                    .as_ref()
293                    .and_then(|r| r.value.as_ref())
294                    .and_then(|r| r.split('/').next())
295            {
296                return check_type_name(resource_type, type_to_check);
297            }
298            false
299        }
300        fhir_type_name => check_type_name(fhir_type_name, type_to_check),
301    }
302}
303
304fn filter_by_type<'a>(type_name: &str, context: &Context<'a>) -> Context<'a> {
305    context.new_context_from(
306        context
307            .values
308            .iter()
309            .filter(|v| check_type(**v, type_name))
310            .copied()
311            .collect(),
312    )
313}
314
315#[derive(Debug, Reflect)]
316#[fhir_type = "Element"]
317struct Reflection {
318    name: String,
319}
320
321async fn evaluate_function<'a>(
322    function: &FunctionInvocation,
323    context: Context<'a>,
324    config: Option<Arc<Config<'a>>>,
325) -> Result<Context<'a>, FHIRPathError> {
326    match function.name.0.as_str() {
327        "resolve" => Ok(context),
328        "where" => evaluate_where(function, context, config).await,
329        "ofType" | "as" => evaluate_of_type(function, &context),
330        "count" => evaluate_count(function, context).await,
331        "upper" | "lower" => evaluate_case(function, context).await,
332        "empty" => evaluate_empty(function, context).await,
333        "join" => evaluate_join(function, context, config).await,
334        "exists" => evaluate_exists(function, context, config).await,
335        "children" => evaluate_children(function, &context),
336        "repeat" => evaluate_repeat(function, context, config).await,
337        "descendants" => evaluate_descendants(context, config).await,
338        "type" => evaluate_type(function, context).await,
339        "first" => evaluate_first(function, &context),
340        "getReferenceKey" => evaluate_get_reference_key(function, context).await,
341        "getResourceKey" => evaluate_get_resource_key(function, context, config).await,
342
343        _ => Err(FHIRPathError::NotImplemented(format!(
344            "Function '{}' is not implemented",
345            function.name.0
346        ))),
347    }
348}
349
350async fn evaluate_where<'a>(
351    function: &FunctionInvocation,
352    context: Context<'a>,
353    config: Option<Arc<Config<'a>>>,
354) -> Result<Context<'a>, FHIRPathError> {
355    validate_arguments(&function.arguments, &Cardinality::One)?;
356
357    let where_condition = &function.arguments[0];
358    let mut new_context = vec![];
359
360    for value in &context.values {
361        let result = evaluate_expression(
362            where_condition,
363            context.new_context_from(vec![*value]),
364            config.clone(),
365        )
366        .await?;
367
368        if result.values.len() > 1 {
369            return Err(FHIRPathError::InternalError(
370                "Where condition did not return a single value".to_string(),
371            ));
372        }
373
374        // Empty result is treated as false.
375        if !result.values.is_empty() && downcast_bool(result.values[0])? {
376            new_context.push(*value);
377        }
378    }
379
380    Ok(context.new_context_from(new_context))
381}
382
383fn evaluate_of_type<'a>(
384    function: &FunctionInvocation,
385    context: &Context<'a>,
386) -> Result<Context<'a>, FHIRPathError> {
387    validate_arguments(&function.arguments, &Cardinality::One)?;
388
389    let type_name = derive_typename(&function.arguments[0])?;
390
391    Ok(filter_by_type(&type_name, context))
392}
393
394async fn evaluate_count<'a>(
395    function: &FunctionInvocation,
396    context: Context<'a>,
397) -> Result<Context<'a>, FHIRPathError> {
398    validate_arguments(&function.arguments, &Cardinality::Zero)?;
399
400    let count: i64 = context
401        .values
402        .len()
403        .try_into()
404        .map_err(|_| FHIRPathError::OperationError(OperationError::SizeOverflow))?;
405
406    Ok(context.new_context_from(vec![
407        context
408            .allocate_literal(FHIRInteger {
409                value: Some(count),
410                ..Default::default()
411            })
412            .await,
413    ]))
414}
415
416async fn evaluate_case<'a>(
417    function: &FunctionInvocation,
418    context: Context<'a>,
419) -> Result<Context<'a>, FHIRPathError> {
420    validate_arguments(&function.arguments, &Cardinality::Zero)?;
421
422    let op = function.name.0.as_str();
423
424    if context.values.is_empty() {
425        return Ok(context.new_context_from(vec![]));
426    }
427
428    if context.values.len() > 1 {
429        return Err(FunctionError::InvalidCardinality(op.to_string(), context.values.len()).into());
430    }
431
432    let input = downcast_string(context.values[0])?;
433
434    let transformed = match op {
435        "upper" => input.to_uppercase(),
436        "lower" => input.to_lowercase(),
437        _ => unreachable!(),
438    };
439
440    Ok(context.new_context_from(vec![
441        context
442            .allocate_literal(FHIRString {
443                value: Some(transformed),
444                ..Default::default()
445            })
446            .await,
447    ]))
448}
449
450async fn evaluate_empty<'a>(
451    function: &FunctionInvocation,
452    context: Context<'a>,
453) -> Result<Context<'a>, FHIRPathError> {
454    validate_arguments(&function.arguments, &Cardinality::Zero)?;
455
456    Ok(context.new_context_from(vec![
457        context
458            .allocate_literal(FHIRBoolean {
459                value: Some(context.values.is_empty()),
460                ..Default::default()
461            })
462            .await,
463    ]))
464}
465
466async fn evaluate_join<'a>(
467    function: &FunctionInvocation,
468    context: Context<'a>,
469    config: Option<Arc<Config<'a>>>,
470) -> Result<Context<'a>, FHIRPathError> {
471    validate_arguments(&function.arguments, &Cardinality::Custom(0, 1))?;
472
473    let separator = if let Some(separator_expression) = function.arguments.first() {
474        let separator_context =
475            evaluate_expression(separator_expression, context.clone(), config).await?;
476
477        if separator_context.values.len() != 1 {
478            return Err(FHIRPathError::OperationError(
479                OperationError::InvalidCardinality,
480            ));
481        }
482
483        downcast_string(separator_context.values[0])?
484    } else {
485        String::new()
486    };
487
488    let joined = context
489        .values
490        .iter()
491        .map(|v| downcast_string(*v))
492        .collect::<Result<Vec<_>, _>>()?
493        .join(&separator);
494
495    Ok(context.new_context_from(vec![
496        context
497            .allocate_literal(FHIRString {
498                value: Some(joined),
499                ..Default::default()
500            })
501            .await,
502    ]))
503}
504
505async fn evaluate_exists<'a>(
506    function: &FunctionInvocation,
507    context: Context<'a>,
508    config: Option<Arc<Config<'a>>>,
509) -> Result<Context<'a>, FHIRPathError> {
510    validate_arguments(&function.arguments, &Cardinality::Many)?;
511
512    if function.arguments.len() > 1 {
513        return Err(FunctionError::InvalidCardinality(
514            "exists".to_string(),
515            function.arguments.len(),
516        )
517        .into());
518    }
519
520    if let Some(condition) = function.arguments.first() {
521        for value in &context.values {
522            let result = evaluate_expression(
523                condition,
524                context.new_context_from(vec![*value]),
525                config.clone(),
526            )
527            .await?;
528
529            if result.values.len() > 1 {
530                return Err(FHIRPathError::InternalError(
531                    "Exists condition did not return a single value".to_string(),
532                ));
533            }
534
535            if !result.values.is_empty() && downcast_bool(result.values[0])? {
536                return Ok(context.new_context_from(vec![
537                    context
538                        .allocate_literal(FHIRBoolean {
539                            value: Some(true),
540                            ..Default::default()
541                        })
542                        .await,
543                ]));
544            }
545        }
546
547        return Ok(context.new_context_from(vec![
548            context
549                .allocate_literal(FHIRBoolean {
550                    value: Some(false),
551                    ..Default::default()
552                })
553                .await,
554        ]));
555    }
556
557    Ok(context.new_context_from(vec![
558        context
559            .allocate_literal(FHIRBoolean {
560                value: Some(!context.values.is_empty()),
561                ..Default::default()
562            })
563            .await,
564    ]))
565}
566
567fn evaluate_children<'a>(
568    function: &FunctionInvocation,
569    context: &Context<'a>,
570) -> Result<Context<'a>, FHIRPathError> {
571    validate_arguments(&function.arguments, &Cardinality::Zero)?;
572
573    let children = context
574        .values
575        .iter()
576        .flat_map(|value| {
577            value
578                .fields()
579                .iter()
580                .filter_map(|f| value.get_field(f).map(|v| v.flatten()))
581                .flatten()
582                .collect::<Vec<_>>()
583        })
584        .collect();
585
586    Ok(context.new_context_from(children))
587}
588
589async fn evaluate_repeat<'a>(
590    function: &FunctionInvocation,
591    context: Context<'a>,
592    config: Option<Arc<Config<'a>>>,
593) -> Result<Context<'a>, FHIRPathError> {
594    validate_arguments(&function.arguments, &Cardinality::One)?;
595
596    let projection = &function.arguments[0];
597    let mut end_result = vec![];
598    let mut cur = context;
599
600    while !cur.values.is_empty() {
601        cur = evaluate_expression(projection, cur, config.clone()).await?;
602        end_result.extend_from_slice(cur.values.as_slice());
603    }
604
605    Ok(cur.new_context_from(end_result))
606}
607
608async fn evaluate_descendants<'a>(
609    context: Context<'a>,
610    config: Option<Arc<Config<'a>>>,
611) -> Result<Context<'a>, FHIRPathError> {
612    let result = evaluate_expression(
613        &Expression::Singular(vec![Term::Invocation(Invocation::Function(
614            FunctionInvocation {
615                name: Identifier("repeat".to_string()),
616                arguments: vec![Expression::Singular(vec![Term::Invocation(
617                    Invocation::Function(FunctionInvocation {
618                        name: Identifier("children".to_string()),
619                        arguments: vec![],
620                    }),
621                )])],
622            },
623        ))]),
624        context,
625        config,
626    )
627    .await?;
628
629    Ok(result)
630}
631
632async fn evaluate_type<'a>(
633    function: &FunctionInvocation,
634    context: Context<'a>,
635) -> Result<Context<'a>, FHIRPathError> {
636    validate_arguments(&function.arguments, &Cardinality::Zero)?;
637
638    let mut next_ctx = Vec::with_capacity(context.values.len());
639
640    for value in &context.values {
641        let type_name = value.fhir_type();
642
643        next_ctx.push(
644            context
645                .allocate_literal(Reflection {
646                    name: type_name.to_string(),
647                })
648                .await,
649        );
650    }
651
652    Ok(context.new_context_from(next_ctx))
653}
654
655fn evaluate_first<'a>(
656    function: &FunctionInvocation,
657    context: &Context<'a>,
658) -> Result<Context<'a>, FHIRPathError> {
659    validate_arguments(&function.arguments, &Cardinality::Zero)?;
660
661    match context.values.first() {
662        Some(value) => Ok(context.new_context_from(vec![*value])),
663        None => Ok(context.new_context_from(vec![])),
664    }
665}
666
667async fn evaluate_get_reference_key<'a>(
668    function: &FunctionInvocation,
669    context: Context<'a>,
670) -> Result<Context<'a>, FHIRPathError> {
671    validate_arguments(&function.arguments, &Cardinality::Custom(0, 1))?;
672
673    let type_to_filter_by = if let Some(resource_type) = function.arguments.first() {
674        Some(derive_typename(resource_type)?)
675    } else {
676        None
677    };
678
679    let ids = context
680        .iter()
681        .filter_map(|value| {
682            let reference = value.as_any().downcast_ref::<Reference>()?;
683
684            let mut pieces = reference
685                .reference
686                .as_ref()
687                .and_then(|r| r.value.as_ref())
688                .map(|r| r.split('/'))?;
689
690            let resource_type = pieces.next()?;
691            let id = pieces.next()?;
692
693            if let Some(type_to_filter_by) = &type_to_filter_by
694                && !check_type_name(resource_type, type_to_filter_by)
695            {
696                return None;
697            }
698
699            Some(FHIRId {
700                value: Some(id.to_string()),
701                ..Default::default()
702            })
703        })
704        .collect::<Vec<_>>();
705
706    let mut next_context = Vec::with_capacity(ids.len());
707
708    for id in ids {
709        next_context.push(context.allocate_literal(id).await);
710    }
711
712    Ok(context.new_context_from(next_context))
713}
714
715async fn evaluate_get_resource_key<'a>(
716    function: &FunctionInvocation,
717    context: Context<'a>,
718    config: Option<Arc<Config<'a>>>,
719) -> Result<Context<'a>, FHIRPathError> {
720    validate_arguments(&function.arguments, &Cardinality::Zero)?;
721
722    let Some(id) = config.and_then(|c| c.resource_id.clone()) else {
723        return Err(FHIRPathError::InternalError(
724            "getResourceKey function requires resource_id in config".to_string(),
725        ));
726    };
727
728    let resource_key = FHIRId {
729        value: Some(id),
730        ..Default::default()
731    };
732
733    Ok(context.new_context_from(vec![context.allocate_literal(resource_key).await]))
734}
735
736fn equal_check<'b>(left: &Context<'b>, right: &Context<'b>) -> Result<bool, FHIRPathError> {
737    if NUMBER_TYPES.contains(left.values[0].fhir_type())
738        && NUMBER_TYPES.contains(right.values[0].fhir_type())
739    {
740        let left_value = downcast_number(left.values[0])?;
741        let right_value = downcast_number(right.values[0])?;
742        #[allow(clippy::float_cmp)]
743        Ok(left_value == right_value)
744    } else if STRING_TYPES.contains(left.values[0].fhir_type())
745        && STRING_TYPES.contains(right.values[0].fhir_type())
746    {
747        let left_value = downcast_string(left.values[0])?;
748        let right_value = downcast_string(right.values[0])?;
749        Ok(left_value == right_value)
750    } else if BOOLEAN_TYPES.contains(left.values[0].fhir_type())
751        && BOOLEAN_TYPES.contains(right.values[0].fhir_type())
752    {
753        let left_value = downcast_bool(left.values[0])?;
754        let right_value = downcast_bool(right.values[0])?;
755        // Suppress clippy warning because strict equality comparison for float values
756        // is intentional and required by the FHIRPath specification.
757        #[allow(clippy::float_cmp)]
758        Ok(left_value == right_value)
759    } else {
760        // https://hl7.org/fhirpath/N1/#conversion for implicit conversion rules todo.
761        //
762        // If types do not match return false.
763        // Should consider implicit conversion rules here but for now
764        // FPs like 'Patient.deceased.exists() and Patient.deceased != false' (deceased is either boolean or dateTime)
765        // Should return false rather than error.
766        Ok(false)
767    }
768}
769
770async fn evaluate_operation<'a>(
771    operation: &Operation,
772    context: Context<'a>,
773    config: Option<Arc<Config<'a>>>,
774) -> Result<Context<'a>, FHIRPathError> {
775    match operation {
776        Operation::Add(left, right) => evaluate_add(left, right, context, config).await,
777        Operation::Subtraction(left, right) => {
778            evaluate_subtraction(left, right, context, config).await
779        }
780        Operation::Multiplication(left, right) => {
781            evaluate_multiplication(left, right, context, config).await
782        }
783        Operation::Division(left, right) => evaluate_division(left, right, context, config).await,
784        Operation::Equal(left, right) => evaluate_equal(left, right, context, config).await,
785        Operation::NotEqual(left, right) => evaluate_not_equal(left, right, context, config).await,
786        Operation::And(left, right) => evaluate_and(left, right, context, config).await,
787        Operation::Or(left, right) => evaluate_or(left, right, context, config).await,
788        Operation::Union(left, right) => evaluate_union(left, right, context, config).await,
789        Operation::Is(expr, ty) => evaluate_is(expr, ty, context, config).await,
790        Operation::As(expr, ty) => evaluate_as(expr, ty, context, config).await,
791        Operation::XOr(left, right) => evaluate_xor(left, right, context, config).await,
792
793        Operation::Modulo(_, _) => not_implemented("Modulo"),
794        Operation::Polarity(_, _) => not_implemented("Polarity"),
795        Operation::DivisionTruncated(_, _) => not_implemented("DivisionTruncated"),
796        Operation::LessThan(left, right) => evaluate_less_than(left, right, context, config).await,
797        Operation::GreaterThan(left, right) => {
798            evaluate_greater_than(left, right, context, config).await
799        }
800        Operation::LessThanEqual(left, right) => {
801            evaluate_less_than_equal(left, right, context, config).await
802        }
803        Operation::GreaterThanEqual(left, right) => {
804            evaluate_greater_than_equal(left, right, context, config).await
805        }
806        Operation::Equivalent(_, _) => not_implemented("Equivalent"),
807        Operation::NotEquivalent(_, _) => not_implemented("NotEquivalent"),
808        Operation::In(_, _) => not_implemented("In"),
809        Operation::Contains(_, _) => not_implemented("Contains"),
810        Operation::Implies(_, _) => not_implemented("Implies"),
811    }
812}
813
814fn not_implemented(name: &'static str) -> Result<Context<'static>, FHIRPathError> {
815    Err(FHIRPathError::NotImplemented(name.to_string()))
816}
817
818async fn evaluate_add<'a>(
819    left: &Expression,
820    right: &Expression,
821    context: Context<'a>,
822    config: Option<Arc<Config<'a>>>,
823) -> Result<Context<'a>, FHIRPathError> {
824    operation_2(left, right, context, config, |left, right| {
825        Box::pin(async move {
826            if NUMBER_TYPES.contains(left.values[0].fhir_type())
827                && NUMBER_TYPES.contains(right.values[0].fhir_type())
828            {
829                let left_value = downcast_number(left.values[0])?;
830                let right_value = downcast_number(right.values[0])?;
831
832                Ok(left.new_context_from(vec![
833                    left.allocate_literal(FHIRDecimal {
834                        value: Some(left_value + right_value),
835                        ..Default::default()
836                    })
837                    .await,
838                ]))
839            } else if STRING_TYPES.contains(left.values[0].fhir_type())
840                && STRING_TYPES.contains(right.values[0].fhir_type())
841            {
842                let left_string = downcast_string(left.values[0])?;
843                let right_string = downcast_string(right.values[0])?;
844
845                Ok(left.new_context_from(vec![
846                    left.allocate_literal(FHIRString {
847                        value: Some(left_string + &right_string),
848                        ..Default::default()
849                    })
850                    .await,
851                ]))
852            } else {
853                Err(FHIRPathError::OperationError(OperationError::TypeMismatch(
854                    left.values[0].fhir_type(),
855                    right.values[0].fhir_type(),
856                )))
857            }
858        })
859    })
860    .await
861}
862
863async fn evaluate_numeric_binary<'a, F>(
864    left: &Expression,
865    right: &Expression,
866    context: Context<'a>,
867    config: Option<Arc<Config<'a>>>,
868    op: F,
869) -> Result<Context<'a>, FHIRPathError>
870where
871    F: FnOnce(f64, f64) -> f64 + Copy + Send + 'static,
872{
873    operation_2(left, right, context, config, move |left, right| {
874        Box::pin(async move {
875            let left_value = downcast_number(left.values[0])?;
876            let right_value = downcast_number(right.values[0])?;
877
878            Ok(left.new_context_from(vec![
879                left.allocate_literal(FHIRDecimal {
880                    value: Some(op(left_value, right_value)),
881                    ..Default::default()
882                })
883                .await,
884            ]))
885        })
886    })
887    .await
888}
889
890async fn evaluate_numerical_comparison<'a, F>(
891    left: &Expression,
892    right: &Expression,
893    context: Context<'a>,
894    config: Option<Arc<Config<'a>>>,
895    op: F,
896) -> Result<Context<'a>, FHIRPathError>
897where
898    F: FnOnce(f64, f64) -> bool + Copy + Send + 'static,
899{
900    operation_2(left, right, context, config, move |left, right| {
901        Box::pin(async move {
902            let left_value = downcast_number(left.values[0])?;
903            let right_value = downcast_number(right.values[0])?;
904
905            Ok(left.new_context_from(vec![
906                left.allocate_literal(FHIRBoolean {
907                    value: Some(op(left_value, right_value)),
908                    ..Default::default()
909                })
910                .await,
911            ]))
912        })
913    })
914    .await
915}
916async fn evaluate_subtraction<'a>(
917    left: &Expression,
918    right: &Expression,
919    context: Context<'a>,
920    config: Option<Arc<Config<'a>>>,
921) -> Result<Context<'a>, FHIRPathError> {
922    evaluate_numeric_binary(left, right, context, config, |l, r| l - r).await
923}
924
925async fn evaluate_multiplication<'a>(
926    left: &Expression,
927    right: &Expression,
928    context: Context<'a>,
929    config: Option<Arc<Config<'a>>>,
930) -> Result<Context<'a>, FHIRPathError> {
931    evaluate_numeric_binary(left, right, context, config, |l, r| l * r).await
932}
933
934async fn evaluate_division<'a>(
935    left: &Expression,
936    right: &Expression,
937    context: Context<'a>,
938    config: Option<Arc<Config<'a>>>,
939) -> Result<Context<'a>, FHIRPathError> {
940    evaluate_numeric_binary(left, right, context, config, |l, r| l / r).await
941}
942
943async fn evaluate_less_than<'a>(
944    left: &Expression,
945    right: &Expression,
946    context: Context<'a>,
947    config: Option<Arc<Config<'a>>>,
948) -> Result<Context<'a>, FHIRPathError> {
949    evaluate_numerical_comparison(left, right, context, config, |l, r| l < r).await
950}
951
952async fn evaluate_less_than_equal<'a>(
953    left: &Expression,
954    right: &Expression,
955    context: Context<'a>,
956    config: Option<Arc<Config<'a>>>,
957) -> Result<Context<'a>, FHIRPathError> {
958    evaluate_numerical_comparison(left, right, context, config, |l, r| l <= r).await
959}
960
961async fn evaluate_greater_than<'a>(
962    left: &Expression,
963    right: &Expression,
964    context: Context<'a>,
965    config: Option<Arc<Config<'a>>>,
966) -> Result<Context<'a>, FHIRPathError> {
967    evaluate_numerical_comparison(left, right, context, config, |l, r| l > r).await
968}
969
970async fn evaluate_greater_than_equal<'a>(
971    left: &Expression,
972    right: &Expression,
973    context: Context<'a>,
974    config: Option<Arc<Config<'a>>>,
975) -> Result<Context<'a>, FHIRPathError> {
976    evaluate_numerical_comparison(left, right, context, config, |l, r| l >= r).await
977}
978
979async fn evaluate_boolean_binary<'a, F>(
980    left: &Expression,
981    right: &Expression,
982    context: Context<'a>,
983    config: Option<Arc<Config<'a>>>,
984    op: F,
985) -> Result<Context<'a>, FHIRPathError>
986where
987    F: FnOnce(bool, bool) -> bool + Copy + Send + 'static,
988{
989    operation_2(left, right, context, config, move |left, right| {
990        Box::pin(async move {
991            let l = downcast_bool(left.values[0])?;
992            let r = downcast_bool(right.values[0])?;
993
994            Ok(left.new_context_from(vec![
995                left.allocate_literal(FHIRBoolean {
996                    value: Some(op(l, r)),
997                    ..Default::default()
998                })
999                .await,
1000            ]))
1001        })
1002    })
1003    .await
1004}
1005
1006async fn evaluate_and<'a>(
1007    left: &Expression,
1008    right: &Expression,
1009    context: Context<'a>,
1010    config: Option<Arc<Config<'a>>>,
1011) -> Result<Context<'a>, FHIRPathError> {
1012    evaluate_boolean_binary(left, right, context, config, |l, r| l && r).await
1013}
1014
1015async fn evaluate_or<'a>(
1016    left: &Expression,
1017    right: &Expression,
1018    context: Context<'a>,
1019    config: Option<Arc<Config<'a>>>,
1020) -> Result<Context<'a>, FHIRPathError> {
1021    evaluate_boolean_binary(left, right, context, config, |l, r| l || r).await
1022}
1023
1024async fn evaluate_xor<'a>(
1025    left: &Expression,
1026    right: &Expression,
1027    context: Context<'a>,
1028    config: Option<Arc<Config<'a>>>,
1029) -> Result<Context<'a>, FHIRPathError> {
1030    evaluate_boolean_binary(left, right, context, config, |l, r| l ^ r).await
1031}
1032
1033async fn evaluate_equality<'a>(
1034    left: &Expression,
1035    right: &Expression,
1036    context: Context<'a>,
1037    config: Option<Arc<Config<'a>>>,
1038    negate: bool,
1039) -> Result<Context<'a>, FHIRPathError> {
1040    operation_2(left, right, context, config, move |left, right| {
1041        Box::pin(async move {
1042            let mut result = equal_check(&left, &right)?;
1043
1044            if negate {
1045                result = !result;
1046            }
1047
1048            Ok(left.new_context_from(vec![
1049                left.allocate_literal(FHIRBoolean {
1050                    value: Some(result),
1051                    ..Default::default()
1052                })
1053                .await,
1054            ]))
1055        })
1056    })
1057    .await
1058}
1059
1060async fn evaluate_equal<'a>(
1061    left: &Expression,
1062    right: &Expression,
1063    context: Context<'a>,
1064    config: Option<Arc<Config<'a>>>,
1065) -> Result<Context<'a>, FHIRPathError> {
1066    evaluate_equality(left, right, context, config, false).await
1067}
1068
1069async fn evaluate_not_equal<'a>(
1070    left: &Expression,
1071    right: &Expression,
1072    context: Context<'a>,
1073    config: Option<Arc<Config<'a>>>,
1074) -> Result<Context<'a>, FHIRPathError> {
1075    evaluate_equality(left, right, context, config, true).await
1076}
1077
1078async fn evaluate_union<'a>(
1079    left: &Expression,
1080    right: &Expression,
1081    context: Context<'a>,
1082    config: Option<Arc<Config<'a>>>,
1083) -> Result<Context<'a>, FHIRPathError> {
1084    operation_n(left, right, context, config, |left, right| {
1085        let mut union = Vec::with_capacity(left.values.len() + right.values.len());
1086        union.extend(left.values.iter());
1087        union.extend(right.values.iter());
1088
1089        Ok(left.new_context_from(union))
1090    })
1091    .await
1092}
1093
1094async fn evaluate_type_operation<'a>(
1095    expression: &Expression,
1096    type_name: &QualifiedIdentifier,
1097    context: Context<'a>,
1098    config: Option<Arc<Config<'a>>>,
1099    return_context: bool,
1100) -> Result<Context<'a>, FHIRPathError> {
1101    let left = evaluate_expression(expression, context, config).await?;
1102
1103    if left.values.len() > 1 {
1104        return Err(FHIRPathError::OperationError(
1105            OperationError::InvalidCardinality,
1106        ));
1107    }
1108
1109    let Some(type_name) = type_name.0.first().map(|id| &id.0) else {
1110        return Ok(left.new_context_from(vec![]));
1111    };
1112
1113    let filtered = filter_by_type(type_name, &left);
1114
1115    if return_context {
1116        Ok(filtered)
1117    } else {
1118        Ok(left.new_context_from(vec![
1119            left.allocate_literal(FHIRBoolean {
1120                value: Some(!filtered.values.is_empty()),
1121                ..Default::default()
1122            })
1123            .await,
1124        ]))
1125    }
1126}
1127
1128async fn evaluate_is<'a>(
1129    expression: &Expression,
1130    type_name: &QualifiedIdentifier,
1131    context: Context<'a>,
1132    config: Option<Arc<Config<'a>>>,
1133) -> Result<Context<'a>, FHIRPathError> {
1134    evaluate_type_operation(expression, type_name, context, config, false).await
1135}
1136
1137async fn evaluate_as<'a>(
1138    expression: &Expression,
1139    type_name: &QualifiedIdentifier,
1140    context: Context<'a>,
1141    config: Option<Arc<Config<'a>>>,
1142) -> Result<Context<'a>, FHIRPathError> {
1143    evaluate_type_operation(expression, type_name, context, config, true).await
1144}
1145
1146fn evaluate_expression<'a>(
1147    ast: &Expression,
1148    context: Context<'a>,
1149    config: Option<Arc<Config<'a>>>,
1150) -> Pin<Box<impl Future<Output = Result<Context<'a>, FHIRPathError>>>> {
1151    Box::pin(async move {
1152        match ast {
1153            Expression::Operation(operation) => {
1154                evaluate_operation(operation, context, config).await
1155            }
1156            Expression::Singular(singular_ast) => {
1157                evaluate_singular(singular_ast, context, config).await
1158            }
1159        }
1160    })
1161}
1162
1163#[derive(Debug)]
1164pub enum ResolvedValue {
1165    Box(Box<dyn MetaValue>),
1166    Arc(Arc<dyn MetaValue>),
1167}
1168
1169impl ResolvedValue {
1170    #[must_use]
1171    pub fn as_meta_value(&self) -> &dyn MetaValue {
1172        match self {
1173            ResolvedValue::Box(b) => &**b,
1174            ResolvedValue::Arc(a) => &**a,
1175        }
1176    }
1177}
1178
1179pub struct Context<'a> {
1180    allocator: Arc<Mutex<allocators::bumpalo::Allocator>>,
1181    values: Vec<&'a dyn MetaValue>,
1182}
1183
1184pub type ResolvedValueFuture = Pin<Box<dyn Future<Output = Option<ResolvedValue>> + Send>>;
1185pub type ResolveValueCallback = Box<dyn Fn(String) -> ResolvedValueFuture + Send + Sync>;
1186
1187pub enum ExternalConstantResolver<'a> {
1188    Function(ResolveValueCallback),
1189    Variable(Arc<HashMap<String, &'a dyn MetaValue>>),
1190}
1191
1192#[derive(Default)]
1193pub struct Config<'a> {
1194    // Used to resolve resource_id in getResourceKey for sql-on-fhir.
1195    resource_id: Option<String>,
1196    variable_resolver: Option<ExternalConstantResolver<'a>>,
1197}
1198
1199impl<'a> Config<'a> {
1200    #[must_use]
1201    pub fn builder() -> Self {
1202        Config::default()
1203    }
1204
1205    #[must_use]
1206    pub fn with_variable_resolver(mut self, resolver: ExternalConstantResolver<'a>) -> Self {
1207        self.variable_resolver = Some(resolver);
1208        self
1209    }
1210
1211    #[must_use]
1212    pub fn with_resource_id(mut self, resource_id: String) -> Self {
1213        self.resource_id = Some(resource_id);
1214        self
1215    }
1216}
1217
1218async fn resolve_external_constant<'a>(
1219    name: &str,
1220    resolver: Option<&ExternalConstantResolver<'a>>,
1221    context: Context<'a>,
1222) -> Result<Context<'a>, FHIRPathError> {
1223    let external_constant = match resolver {
1224        Some(ExternalConstantResolver::Function(func)) => {
1225            let result = func(name.to_string()).await;
1226
1227            if let Some(result) = result {
1228                Some(context.allocate(result).await)
1229            } else {
1230                None
1231            }
1232        }
1233        Some(ExternalConstantResolver::Variable(map)) => map.get(name).copied(),
1234        None => None,
1235    };
1236
1237    if let Some(result) = external_constant {
1238        return Ok(context.new_context_from(vec![result]));
1239    }
1240    Ok(context.new_context_from(vec![]))
1241}
1242
1243impl<'a> IntoIterator for &'a Context<'_> {
1244    type Item = &'a dyn haste_reflect::MetaValue;
1245
1246    type IntoIter = std::boxed::Box<
1247        dyn std::iter::Iterator<Item = &'a (dyn haste_reflect::MetaValue + 'static)> + 'a,
1248    >;
1249
1250    fn into_iter(self) -> Self::IntoIter {
1251        self.iter()
1252    }
1253}
1254
1255impl<'a> Context<'a> {
1256    fn new(
1257        values: Vec<&'a dyn MetaValue>,
1258        allocator: Arc<Mutex<allocators::bumpalo::Allocator>>,
1259    ) -> Self {
1260        Self { allocator, values }
1261    }
1262    fn new_context_from(&self, values: Vec<&'a dyn MetaValue>) -> Self {
1263        Self {
1264            allocator: self.allocator.clone(),
1265            values,
1266        }
1267    }
1268    async fn allocate(&self, value: ResolvedValue) -> &'a dyn MetaValue {
1269        self.allocator.lock().await.allocate_resolved(value)
1270    }
1271
1272    async fn allocate_literal<T: MetaValue>(&self, value: T) -> &'a dyn MetaValue {
1273        self.allocator.lock().await.allocate_literal(value)
1274    }
1275    #[must_use]
1276    pub fn iter(&'a self) -> Box<dyn Iterator<Item = &'a dyn MetaValue> + 'a> {
1277        Box::new(self.values.iter().copied())
1278    }
1279}
1280
1281impl Clone for Context<'_> {
1282    fn clone(&self) -> Self {
1283        Self {
1284            allocator: self.allocator.clone(),
1285            values: self.values.clone(),
1286        }
1287    }
1288}
1289
1290pub struct FPEngine {}
1291
1292static AST: LazyLock<Arc<DashMap<String, Expression>>> = LazyLock::new(|| Arc::new(DashMap::new()));
1293
1294fn get_ast(
1295    path: &str,
1296) -> Result<dashmap::mapref::one::Ref<'static, String, Expression>, FHIRPathError> {
1297    let ast: dashmap::mapref::one::Ref<'_, String, Expression> =
1298        if let Some(expression_ast) = AST.get(path) {
1299            expression_ast
1300        } else {
1301            AST.insert(path.to_string(), parser::parse(path)?);
1302            AST.get(path).ok_or_else(|| {
1303                FHIRPathError::InternalError("Failed to find path post insert".to_string())
1304            })?
1305        };
1306
1307    Ok(ast)
1308}
1309
1310impl Default for FPEngine {
1311    fn default() -> Self {
1312        Self::new()
1313    }
1314}
1315
1316impl FPEngine {
1317    #[must_use]
1318    pub fn new() -> Self {
1319        Self {}
1320    }
1321
1322    /// Evaluate a `FHIRPath` expression against a context.
1323    /// The context is a vector of references to `MetaValue` objects.
1324    /// The path is a `FHIRPath` expression.
1325    /// The result is a vector of references to `MetaValue` objects.
1326    ///
1327    /// # Errors
1328    ///
1329    /// Returns a [`FHIRPathError`] if the expression parsing fails (bad AST)
1330    /// or if the evaluation engine encounters an error during execution.
1331    pub async fn evaluate<'a, 'b>(
1332        &self,
1333        path: &str,
1334        values: Vec<&'a dyn MetaValue>,
1335    ) -> Result<Context<'b>, FHIRPathError>
1336    where
1337        'a: 'b,
1338    {
1339        let ast = get_ast(path)?;
1340
1341        // Store created.
1342        let allocator: Arc<Mutex<allocators::bumpalo::Allocator>> =
1343            Arc::new(Mutex::new(allocators::bumpalo::Allocator::new()));
1344
1345        let context = Context::new(values, allocator.clone());
1346
1347        let result = evaluate_expression(&ast, context, None).await?;
1348        Ok(result)
1349    }
1350
1351    /// Evaluate a `FHIRPath` expression against a context.
1352    /// The context is a vector of references to `MetaValue` objects.
1353    /// The path is a `FHIRPath` expression.
1354    /// The result is a vector of references to `MetaValue` objects.
1355    ///
1356    /// # Errors
1357    ///
1358    /// Returns a [`FHIRPathError`] if the expression parsing fails (bad AST)
1359    /// or if the evaluation engine encounters an error during execution.
1360    pub async fn evaluate_with_config<'a, 'b>(
1361        &self,
1362        path: &str,
1363        values: Vec<&'a dyn MetaValue>,
1364        config: Arc<Config<'b>>,
1365    ) -> Result<Context<'b>, FHIRPathError>
1366    where
1367        'a: 'b,
1368    {
1369        let ast = get_ast(path)?;
1370
1371        // Store created.
1372        let allocator = Arc::new(Mutex::new(allocators::bumpalo::Allocator::new()));
1373
1374        let context = Context::new(values, allocator.clone());
1375
1376        let result = evaluate_expression(&ast, context, Some(config)).await?;
1377
1378        Ok(result)
1379    }
1380}
1381
1382#[cfg(test)]
1383mod tests {
1384    use super::*;
1385    use haste_fhir_model::r4::{
1386        datetime::DateTime,
1387        generated::{
1388            resources::{
1389                Bundle, Group, GroupMember, Patient, PatientDeceasedTypeChoice, PatientLink,
1390                Resource, SearchParameter,
1391            },
1392            types::{
1393                Extension, ExtensionValueTypeChoice, FHIRDateTime, FHIRString, FHIRUri, HumanName,
1394                Identifier, Reference,
1395            },
1396        },
1397    };
1398
1399    use haste_reflect_derive::Reflect;
1400
1401    #[derive(Reflect, Debug)]
1402    #[fhir_type = "BackboneElement"]
1403    struct C {
1404        c: String,
1405    }
1406
1407    #[derive(Reflect, Debug)]
1408    #[fhir_type = "BackboneElement"]
1409    struct B {
1410        b: Vec<Box<C>>,
1411    }
1412
1413    #[derive(Reflect, Debug)]
1414    #[fhir_type = "BackboneElement"]
1415    struct A {
1416        a: Vec<Box<B>>,
1417    }
1418
1419    fn load_search_parameters() -> Vec<SearchParameter> {
1420        let json =
1421            include_str!("../../artifacts/artifacts/r4/hl7/minified/search-parameters.min.json");
1422        let bundle = serde_json::from_str::<Bundle>(json).unwrap();
1423
1424        let search_parameters: Vec<SearchParameter> = bundle
1425            .entry
1426            .unwrap_or_else(|| Vec::new())
1427            .into_iter()
1428            .map(|e| e.resource)
1429            .filter(|e| e.is_some())
1430            .filter_map(|e| match e {
1431                Some(k) => match *k {
1432                    Resource::SearchParameter(sp) => Some(sp),
1433                    _ => None,
1434                },
1435                _ => None,
1436            })
1437            .collect();
1438
1439        search_parameters
1440    }
1441
1442    #[tokio::test]
1443    async fn filter_typechoice_test() {
1444        let patient = Patient {
1445            id: Some("patient-id".to_string()),
1446            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1447                value: Some(true),
1448                ..Default::default()
1449            }))),
1450            ..Default::default()
1451        };
1452
1453        let engine = FPEngine::new();
1454        let result = engine
1455            .evaluate("(Patient.deceased.ofType(dateTime))", vec![&patient])
1456            .await
1457            .unwrap();
1458
1459        assert_eq!(result.values.len(), 0);
1460
1461        let result = engine
1462            .evaluate("(Patient.deceased.ofType(boolean))", vec![&patient])
1463            .await
1464            .unwrap();
1465
1466        let value = result.values[0];
1467        let boolean_value: &FHIRBoolean = value
1468            .as_any()
1469            .downcast_ref::<FHIRBoolean>()
1470            .expect("Failed to downcast to FHIRBoolean");
1471
1472        assert_eq!(boolean_value.value, Some(true));
1473
1474        let patient = Patient {
1475            id: Some("patient-id".to_string()),
1476            deceased: Some(PatientDeceasedTypeChoice::DateTime(Box::new(
1477                FHIRDateTime {
1478                    value: Some(DateTime::Year(1980)),
1479                    ..Default::default()
1480                },
1481            ))),
1482            ..Default::default()
1483        };
1484
1485        let result = engine
1486            .evaluate("(Patient.deceased.ofType(boolean))", vec![&patient])
1487            .await
1488            .unwrap();
1489
1490        assert_eq!(result.values.len(), 0);
1491
1492        let result = engine
1493            .evaluate("(Patient.deceased.ofType(dateTime))", vec![&patient])
1494            .await
1495            .unwrap();
1496
1497        assert_eq!(result.values.len(), 1);
1498
1499        let value = result.values[0];
1500        let datetime_value: &FHIRDateTime = value
1501            .as_any()
1502            .downcast_ref::<FHIRDateTime>()
1503            .expect("Failed to downcast to FHIRDateTime");
1504
1505        assert_eq!(datetime_value.value, Some(DateTime::Year(1980)));
1506    }
1507
1508    #[tokio::test]
1509    async fn test_variable_resolution() {
1510        let engine = FPEngine::new();
1511        let patient = Patient {
1512            id: Some("my-patient".to_string()),
1513            ..Default::default()
1514        };
1515        let config = Arc::new(
1516            Config::builder().with_variable_resolver(ExternalConstantResolver::Variable(Arc::new(
1517                vec![("patient".to_string(), &patient as &dyn MetaValue)]
1518                    .into_iter()
1519                    .collect(),
1520            ))),
1521        );
1522
1523        let result = engine
1524            .evaluate_with_config("%patient", vec![], config.clone())
1525            .await
1526            .unwrap();
1527
1528        assert_eq!(result.values.len(), 1);
1529        let p = result.values[0].as_any().downcast_ref::<Patient>().unwrap();
1530
1531        assert_eq!(p.id, patient.id);
1532
1533        let result_failed = engine
1534            .evaluate_with_config("%nobody", vec![], config)
1535            .await
1536            .unwrap();
1537
1538        assert_eq!(result_failed.values.len(), 0);
1539    }
1540
1541    #[tokio::test]
1542    async fn test_where_clause() {
1543        let engine = FPEngine::new();
1544        let mut patient = Patient::default();
1545        let mut identifier = Identifier::default();
1546        let extension = Extension {
1547            id: None,
1548            url: "test-extension".to_string(),
1549            extension: None,
1550            value: Some(ExtensionValueTypeChoice::String(Box::new(FHIRString {
1551                id: None,
1552                extension: None,
1553                value: Some("example value".to_string()),
1554            }))),
1555        };
1556        identifier.value = Some(Box::new(FHIRString {
1557            id: None,
1558            extension: Some(vec![extension]),
1559            value: Some("12345".to_string()),
1560        }));
1561        patient.identifier_ = Some(vec![identifier]);
1562
1563        let context = engine
1564            .evaluate(
1565                "$this.identifier.value.where($this.extension.value.exists())",
1566                vec![&patient],
1567            )
1568            .await;
1569
1570        assert_eq!(context.unwrap().values.len(), 1);
1571
1572        let context = engine
1573            .evaluate(
1574                "$this.identifier.value.where($this.extension.extension.exists())",
1575                vec![&patient],
1576            )
1577            .await;
1578        assert_eq!(context.unwrap().values.len(), 0);
1579    }
1580
1581    #[tokio::test]
1582    async fn test_all_parameters() {
1583        let search_parameters = load_search_parameters();
1584        for param in search_parameters.iter() {
1585            if let Some(expression) = &param.expression {
1586                let engine = FPEngine::new();
1587                let context = engine
1588                    .evaluate(expression.value.as_ref().unwrap().as_str(), vec![])
1589                    .await;
1590
1591                if let Err(err) = context {
1592                    panic!(
1593                        "Failed to evaluate search parameter '{}': {}",
1594                        expression.value.as_ref().unwrap(),
1595                        err
1596                    );
1597                }
1598            }
1599        }
1600    }
1601
1602    fn test_patient() -> Patient {
1603        let mut patient = Patient::default();
1604        let mut name = HumanName::default();
1605        name.given = Some(vec![FHIRString {
1606            id: None,
1607            extension: None,
1608            value: Some("Bob".to_string()),
1609        }]);
1610
1611        let mut mrn_identifier = Identifier::default();
1612        mrn_identifier.value = Some(Box::new(FHIRString {
1613            id: None,
1614            extension: None,
1615            value: Some("mrn-12345".to_string()),
1616        }));
1617        mrn_identifier.system = Some(Box::new(FHIRUri {
1618            id: None,
1619            extension: None,
1620            value: Some("mrn".to_string()),
1621        }));
1622
1623        let mut ssn_identifier = Identifier::default();
1624        ssn_identifier.value = Some(Box::new(FHIRString {
1625            id: None,
1626            extension: None,
1627            value: Some("ssn-12345".to_string()),
1628        }));
1629        ssn_identifier.system = Some(Box::new(FHIRUri {
1630            id: None,
1631            extension: None,
1632            value: Some("ssn".to_string()),
1633        }));
1634
1635        mrn_identifier.system = Some(Box::new(FHIRUri {
1636            id: None,
1637            extension: None,
1638            value: Some("mrn".to_string()),
1639        }));
1640
1641        patient.identifier_ = Some(vec![mrn_identifier, ssn_identifier]);
1642        patient.name = Some(vec![name]);
1643        patient
1644    }
1645
1646    #[tokio::test]
1647    async fn indexing_tests() {
1648        let engine = FPEngine::new();
1649        let patient = test_patient();
1650
1651        let given_name = engine
1652            .evaluate("$this.name.given[0]", vec![&patient])
1653            .await
1654            .unwrap();
1655
1656        assert_eq!(given_name.values.len(), 1);
1657        let value = given_name.values[0];
1658        let name: &FHIRString = value
1659            .as_any()
1660            .downcast_ref::<FHIRString>()
1661            .expect("Failed to downcast to FHIRString");
1662
1663        assert_eq!(name.value.as_deref(), Some("Bob"));
1664
1665        let ssn_identifier = engine
1666            .evaluate("$this.identifier[1]", vec![&patient])
1667            .await
1668            .unwrap();
1669
1670        assert_eq!(ssn_identifier.values.len(), 1);
1671        let value = ssn_identifier.values[0];
1672        let identifier: &Identifier = value
1673            .as_any()
1674            .downcast_ref::<Identifier>()
1675            .expect("Failed to downcast to Identifier");
1676
1677        assert_eq!(
1678            identifier.value.as_ref().unwrap().value.as_deref(),
1679            Some("ssn-12345")
1680        );
1681
1682        let all_identifiers = engine
1683            .evaluate("$this.identifier", vec![&patient])
1684            .await
1685            .unwrap();
1686        assert_eq!(all_identifiers.values.len(), 2);
1687    }
1688
1689    #[tokio::test]
1690    async fn where_testing() {
1691        let engine = FPEngine::new();
1692        let patient = test_patient();
1693
1694        let name_where_clause = engine
1695            .evaluate(
1696                "$this.name.given.where($this.value = 'Bob')",
1697                vec![&patient],
1698            )
1699            .await
1700            .unwrap();
1701
1702        assert_eq!(name_where_clause.values.len(), 1);
1703        let value = name_where_clause.values[0];
1704        let name: &FHIRString = value
1705            .as_any()
1706            .downcast_ref::<FHIRString>()
1707            .expect("Failed to downcast to FHIRString");
1708
1709        assert_eq!(name.value.as_deref(), Some("Bob"));
1710
1711        let ssn_identifier_clause = engine
1712            .evaluate(
1713                "$this.identifier.where($this.system.value = 'ssn')",
1714                vec![&patient],
1715            )
1716            .await
1717            .unwrap();
1718        assert_eq!(ssn_identifier_clause.values.len(), 1);
1719
1720        let ssn_identifier = ssn_identifier_clause.values[0]
1721            .as_any()
1722            .downcast_ref::<Identifier>()
1723            .expect("Failed to downcast to Identifier");
1724
1725        assert_eq!(
1726            ssn_identifier.value.as_ref().unwrap().value.as_deref(),
1727            Some("ssn-12345")
1728        );
1729    }
1730
1731    #[tokio::test]
1732    async fn test_equality() {
1733        let engine = FPEngine::new();
1734
1735        // String tests
1736        let string_equal = engine.evaluate("'test' = 'test'", vec![]).await.unwrap();
1737        for r in string_equal.iter() {
1738            let b: bool = r
1739                .as_any()
1740                .downcast_ref::<FHIRBoolean>()
1741                .unwrap()
1742                .value
1743                .unwrap()
1744                .clone();
1745            assert_eq!(b, true);
1746        }
1747        let string_unequal = engine.evaluate("'invalid' = 'test'", vec![]).await.unwrap();
1748        for r in string_unequal.iter() {
1749            let b: bool = r
1750                .as_any()
1751                .downcast_ref::<FHIRBoolean>()
1752                .unwrap()
1753                .value
1754                .unwrap()
1755                .clone();
1756            assert_eq!(b, false);
1757        }
1758
1759        // Number tests
1760        let number_equal = engine.evaluate("12 = 12", vec![]).await.unwrap();
1761        for r in number_equal.iter() {
1762            let b: bool = r
1763                .as_any()
1764                .downcast_ref::<FHIRBoolean>()
1765                .unwrap()
1766                .value
1767                .unwrap()
1768                .clone();
1769            assert_eq!(b, true);
1770        }
1771        let number_unequal = engine.evaluate("13 = 12", vec![]).await.unwrap();
1772        for r in number_unequal.iter() {
1773            let b: bool = r
1774                .as_any()
1775                .downcast_ref::<FHIRBoolean>()
1776                .unwrap()
1777                .value
1778                .unwrap()
1779                .clone();
1780            assert_eq!(b, false);
1781        }
1782
1783        // Boolean tests
1784        let bool_equal = engine.evaluate("false = false", vec![]).await.unwrap();
1785        for r in bool_equal.iter() {
1786            let b: bool = r
1787                .as_any()
1788                .downcast_ref::<FHIRBoolean>()
1789                .unwrap()
1790                .value
1791                .unwrap()
1792                .clone();
1793            assert_eq!(b, true);
1794        }
1795        let bool_unequal = engine.evaluate("false = true", vec![]).await.unwrap();
1796        for r in bool_unequal.iter() {
1797            let b: bool = r
1798                .as_any()
1799                .downcast_ref::<FHIRBoolean>()
1800                .unwrap()
1801                .value
1802                .unwrap()
1803                .clone();
1804            assert_eq!(b, false);
1805        }
1806
1807        // Nested Equality tests
1808        let bool_equal = engine.evaluate("12 = 13 = false", vec![]).await.unwrap();
1809        for r in bool_equal.iter() {
1810            let b: bool = r
1811                .as_any()
1812                .downcast_ref::<FHIRBoolean>()
1813                .unwrap()
1814                .value
1815                .unwrap()
1816                .clone();
1817            assert_eq!(b, true);
1818        }
1819        let bool_unequal = engine.evaluate("12 = 13 = true", vec![]).await.unwrap();
1820        for r in bool_unequal.iter() {
1821            let b: bool = r
1822                .as_any()
1823                .downcast_ref::<FHIRBoolean>()
1824                .unwrap()
1825                .value
1826                .unwrap()
1827                .clone();
1828            assert_eq!(b, false);
1829        }
1830        let bool_unequal = engine.evaluate("12 = (13 - 1)", vec![]).await.unwrap();
1831        for r in bool_unequal.iter() {
1832            let b: bool = r
1833                .as_any()
1834                .downcast_ref::<FHIRBoolean>()
1835                .unwrap()
1836                .value
1837                .unwrap()
1838                .clone();
1839
1840            assert_eq!(b, true);
1841        }
1842    }
1843
1844    #[tokio::test]
1845    async fn test_string_concat() {
1846        let engine = FPEngine::new();
1847        let patient = test_patient();
1848
1849        let simple_result = engine.evaluate("'Hello' + ' World'", vec![]).await.unwrap();
1850        for r in simple_result.iter() {
1851            let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1852            assert_eq!(s.value, Some("Hello World".to_string()));
1853        }
1854
1855        let simple_result = engine
1856            .evaluate("$this.name.given + ' Miller'", vec![&patient])
1857            .await
1858            .unwrap();
1859        for r in simple_result.iter() {
1860            let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1861            assert_eq!(s.value, Some("Bob Miller".to_string()));
1862        }
1863    }
1864
1865    #[tokio::test]
1866    async fn test_simple() {
1867        let root = A {
1868            a: vec![Box::new(B {
1869                b: vec![Box::new(C {
1870                    c: "whatever".to_string(),
1871                })],
1872            })],
1873        };
1874
1875        let engine = FPEngine::new();
1876        let result = engine.evaluate("a.b.c", vec![&root]).await.unwrap();
1877
1878        let strings: Vec<&String> = result
1879            .iter()
1880            .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1881            .collect();
1882
1883        assert_eq!(strings, vec!["whatever"]);
1884    }
1885
1886    #[tokio::test]
1887    async fn allocation() {
1888        let engine = FPEngine::new();
1889        let result = engine.evaluate("'asdf'", vec![]).await.unwrap();
1890
1891        for r in result.iter() {
1892            let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1893
1894            assert_eq!(s.value, Some("asdf".to_string()));
1895        }
1896    }
1897
1898    #[tokio::test]
1899    async fn order_operation() {
1900        let engine = FPEngine::new();
1901        let result = engine.evaluate("45 + 2  * 3", vec![]).await.unwrap();
1902
1903        for r in result.iter() {
1904            let s = r.as_any().downcast_ref::<FHIRDecimal>().unwrap().clone();
1905
1906            assert_eq!(s.value, Some(51.0));
1907        }
1908    }
1909
1910    #[tokio::test]
1911    async fn xor_operation() {
1912        let engine = FPEngine::new();
1913        let result = engine.evaluate("true xor true", vec![]).await.unwrap();
1914
1915        for r in result.iter() {
1916            let b: bool = r
1917                .as_any()
1918                .downcast_ref::<FHIRBoolean>()
1919                .unwrap()
1920                .value
1921                .unwrap()
1922                .clone();
1923
1924            assert_eq!(b, false);
1925        }
1926    }
1927
1928    #[tokio::test]
1929    async fn domain_resource_filter() {
1930        let engine = FPEngine::new();
1931
1932        let patient =
1933            serde_json::from_str::<Resource>(r#"{"id": "patient-id", "resourceType": "Patient"}"#)
1934                .unwrap();
1935        let result = engine
1936            .evaluate("Resource.id", vec![&patient])
1937            .await
1938            .unwrap();
1939        let ids: Vec<&String> = result
1940            .iter()
1941            .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1942            .collect();
1943
1944        assert_eq!(ids.len(), 1);
1945        assert_eq!(ids[0], "patient-id");
1946
1947        let result2 = engine
1948            .evaluate("DomainResource.id", vec![&patient])
1949            .await
1950            .unwrap();
1951        let ids2: Vec<&String> = result2
1952            .iter()
1953            .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1954            .collect();
1955        assert_eq!(ids2.len(), 1);
1956        assert_eq!(ids2[0], "patient-id");
1957    }
1958
1959    #[tokio::test]
1960    async fn type_test() {
1961        let engine = FPEngine::new();
1962        let patient = Patient::default();
1963
1964        let result = engine
1965            .evaluate("$this.type().name", vec![&patient])
1966            .await
1967            .unwrap();
1968        let ids: Vec<&String> = result
1969            .iter()
1970            .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1971            .collect();
1972
1973        assert_eq!(ids.len(), 1);
1974        assert_eq!(ids[0], "Patient");
1975    }
1976
1977    #[tokio::test]
1978    async fn resolve_test() {
1979        let engine = FPEngine::new();
1980        let observation = serde_json::from_str::<Resource>(r#"
1981             {
1982                "resourceType": "Observation",
1983                "id": "f001",
1984                "text": {
1985                    "status": "generated",
1986                    "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>"
1987                },
1988                "identifier": [
1989                    {
1990                    "use": "official",
1991                    "system": "http://www.bmc.nl/zorgportal/identifiers/observations",
1992                    "value": "6323"
1993                    }
1994                ],
1995                "status": "final",
1996                "code": {
1997                    "coding": [
1998                    {
1999                        "system": "http://loinc.org",
2000                        "code": "15074-8",
2001                        "display": "Glucose [Moles/volume] in Blood"
2002                    }
2003                    ]
2004                },
2005                "subject": {
2006                    "reference": "Patient/f001",
2007                    "display": "P. van de Heuvel"
2008                },
2009                "effectivePeriod": {
2010                    "start": "2013-04-02T09:30:10+01:00"
2011                },
2012                "issued": "2013-04-03T15:30:10+01:00",
2013                "performer": [
2014                    {
2015                    "reference": "Practitioner/f005",
2016                    "display": "A. Langeveld"
2017                    }
2018                ],
2019                "valueQuantity": {
2020                    "value": 6.3,
2021                    "unit": "mmol/l",
2022                    "system": "http://unitsofmeasure.org",
2023                    "code": "mmol/L"
2024                },
2025                "interpretation": [
2026                    {
2027                    "coding": [
2028                        {
2029                        "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
2030                        "code": "H",
2031                        "display": "High"
2032                        }
2033                    ]
2034                    }
2035                ],
2036                "referenceRange": [
2037                    {
2038                    "low": {
2039                        "value": 3.1,
2040                        "unit": "mmol/l",
2041                        "system": "http://unitsofmeasure.org",
2042                        "code": "mmol/L"
2043                    },
2044                    "high": {
2045                        "value": 6.2,
2046                        "unit": "mmol/l",
2047                        "system": "http://unitsofmeasure.org",
2048                        "code": "mmol/L"
2049                    }
2050                    }
2051                ]
2052                }
2053            "#).unwrap();
2054
2055        let result = engine
2056            .evaluate(
2057                "Observation.subject.where(resolve() is Patient)",
2058                vec![&observation],
2059            )
2060            .await
2061            .unwrap();
2062
2063        let references: Vec<&Reference> = result
2064            .iter()
2065            .map(|r| r.as_any().downcast_ref::<Reference>().unwrap())
2066            .collect();
2067
2068        assert_eq!(references.len(), 1);
2069        assert_eq!(
2070            references[0].reference.as_ref().unwrap().value,
2071            Some("Patient/f001".to_string())
2072        );
2073    }
2074
2075    #[tokio::test]
2076    async fn children_test() {
2077        let engine = FPEngine::new();
2078        let patient = Patient {
2079            name: Some(vec![HumanName {
2080                given: Some(vec![FHIRString {
2081                    value: Some("Alice".to_string()),
2082                    ..Default::default()
2083                }]),
2084                ..Default::default()
2085            }]),
2086            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2087                value: Some(true),
2088                ..Default::default()
2089            }))),
2090            ..Default::default()
2091        };
2092
2093        let result = engine
2094            .evaluate("$this.children()", vec![&patient])
2095            .await
2096            .unwrap();
2097
2098        assert_eq!(result.values.len(), 2);
2099        assert_eq!(
2100            result
2101                .values
2102                .iter()
2103                .map(|v| v.fhir_type())
2104                .collect::<Vec<_>>(),
2105            vec!["HumanName", "boolean"]
2106        );
2107    }
2108
2109    #[tokio::test]
2110    async fn repeat_test() {
2111        let engine = FPEngine::new();
2112        let patient = Patient {
2113            name: Some(vec![HumanName {
2114                given: Some(vec![FHIRString {
2115                    value: Some("Alice".to_string()),
2116                    ..Default::default()
2117                }]),
2118                ..Default::default()
2119            }]),
2120            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2121                value: Some(true),
2122                ..Default::default()
2123            }))),
2124            ..Default::default()
2125        };
2126
2127        let result = engine
2128            .evaluate("$this.name.given", vec![&patient])
2129            .await
2130            .unwrap();
2131
2132        assert_eq!(result.values.len(), 1);
2133
2134        assert_eq!(result.values[0].fhir_type(), "string");
2135
2136        let result = engine
2137            .evaluate("$this.repeat(children())", vec![&patient])
2138            .await
2139            .unwrap();
2140
2141        assert_eq!(
2142            result
2143                .values
2144                .iter()
2145                .map(|v| v.fhir_type())
2146                .collect::<Vec<_>>(),
2147            vec![
2148                "HumanName",
2149                "boolean",
2150                "string",
2151                "http://hl7.org/fhirpath/System.Boolean",
2152                "http://hl7.org/fhirpath/System.String"
2153            ]
2154        );
2155    }
2156    #[tokio::test]
2157    async fn descendants_test() {
2158        let engine = FPEngine::new();
2159        let patient = Patient {
2160            name: Some(vec![HumanName {
2161                given: Some(vec![FHIRString {
2162                    value: Some("Alice".to_string()),
2163                    ..Default::default()
2164                }]),
2165                ..Default::default()
2166            }]),
2167            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2168                value: Some(true),
2169                ..Default::default()
2170            }))),
2171            ..Default::default()
2172        };
2173        let result = engine
2174            .evaluate("descendants()", vec![&patient])
2175            .await
2176            .unwrap();
2177
2178        assert_eq!(
2179            result
2180                .values
2181                .iter()
2182                .map(|v| v.fhir_type())
2183                .collect::<Vec<_>>(),
2184            vec![
2185                "HumanName",
2186                "boolean",
2187                "string",
2188                "http://hl7.org/fhirpath/System.Boolean",
2189                "http://hl7.org/fhirpath/System.String"
2190            ]
2191        );
2192    }
2193
2194    #[tokio::test]
2195    async fn descendants_test_filter() {
2196        let engine = FPEngine::new();
2197        let patient = Patient {
2198            link: Some(vec![PatientLink {
2199                other: Box::new(Reference {
2200                    reference: Some(Box::new(FHIRString {
2201                        value: Some("Patient/123".to_string()),
2202                        ..Default::default()
2203                    })),
2204                    ..Default::default()
2205                }),
2206                ..Default::default()
2207            }]),
2208            name: Some(vec![HumanName {
2209                given: Some(vec![FHIRString {
2210                    value: Some("Alice".to_string()),
2211                    ..Default::default()
2212                }]),
2213                ..Default::default()
2214            }]),
2215            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2216                value: Some(true),
2217                ..Default::default()
2218            }))),
2219            ..Default::default()
2220        };
2221        let result = engine
2222            .evaluate("descendants()", vec![&patient])
2223            .await
2224            .unwrap();
2225
2226        assert_eq!(
2227            result
2228                .values
2229                .iter()
2230                .map(|v| v.fhir_type())
2231                .collect::<Vec<_>>(),
2232            vec![
2233                "HumanName",
2234                "boolean",
2235                "BackboneElement",
2236                "string",
2237                "http://hl7.org/fhirpath/System.Boolean",
2238                "Reference",
2239                "http://hl7.org/fhirpath/System.String",
2240                "string",
2241                "http://hl7.org/fhirpath/System.String"
2242            ]
2243        );
2244
2245        let result = engine
2246            .evaluate("descendants().ofType(Reference)", vec![&patient])
2247            .await
2248            .unwrap();
2249
2250        assert_eq!(
2251            result
2252                .values
2253                .iter()
2254                .map(|v| v.fhir_type())
2255                .collect::<Vec<_>>(),
2256            vec!["Reference",]
2257        );
2258
2259        let value = result.values[0]
2260            .as_any()
2261            .downcast_ref::<Reference>()
2262            .unwrap();
2263
2264        assert_eq!(
2265            value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2266            "Patient/123"
2267        );
2268    }
2269
2270    #[tokio::test]
2271    async fn try_unsafe_set_from_ref() {
2272        let engine = FPEngine::new();
2273        let patient = Patient {
2274            link: Some(vec![PatientLink {
2275                other: Box::new(Reference {
2276                    reference: Some(Box::new(FHIRString {
2277                        value: Some("Patient/123".to_string()),
2278                        ..Default::default()
2279                    })),
2280                    ..Default::default()
2281                }),
2282                ..Default::default()
2283            }]),
2284            name: Some(vec![HumanName {
2285                given: Some(vec![FHIRString {
2286                    value: Some("Alice".to_string()),
2287                    ..Default::default()
2288                }]),
2289                ..Default::default()
2290            }]),
2291            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2292                value: Some(true),
2293                ..Default::default()
2294            }))),
2295            ..Default::default()
2296        };
2297
2298        let result = engine
2299            .evaluate("descendants().ofType(Reference)", vec![&patient])
2300            .await
2301            .unwrap();
2302
2303        assert_eq!(
2304            result
2305                .values
2306                .iter()
2307                .map(|v| v.fhir_type())
2308                .collect::<Vec<_>>(),
2309            vec!["Reference",]
2310        );
2311
2312        let value = result.values[0]
2313            .as_any()
2314            .downcast_ref::<Reference>()
2315            .unwrap();
2316
2317        assert_eq!(
2318            value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2319            "Patient/123"
2320        );
2321
2322        // An example for use in transaction processing where we have a reference to an object
2323        // but need to modify it in place.
2324        unsafe {
2325            let r = value as *const Reference;
2326            let mut_ptr = r as *mut Reference;
2327
2328            (*mut_ptr).reference = Some(Box::new(FHIRString {
2329                value: Some("Patient/456".to_string()),
2330                ..Default::default()
2331            }));
2332        }
2333
2334        assert_eq!(
2335            value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2336            "Patient/456"
2337        );
2338
2339        assert_eq!(
2340            patient.link.as_ref().unwrap()[0]
2341                .other
2342                .reference
2343                .as_ref()
2344                .unwrap()
2345                .value
2346                .as_ref()
2347                .unwrap(),
2348            "Patient/456"
2349        );
2350    }
2351
2352    #[tokio::test]
2353    async fn test_external_constant_function() {
2354        let engine = FPEngine::new();
2355
2356        let config = Arc::new(Config::builder().with_variable_resolver(
2357            ExternalConstantResolver::Function(Box::new(|v| {
2358                Box::pin(async move {
2359                    match v.as_ref() {
2360                        "test_variable" => Some(ResolvedValue::Box(Box::new(Patient {
2361                            name: Some(vec![HumanName {
2362                                given: Some(vec![FHIRString {
2363                                    value: Some("Paul".to_string()),
2364                                    ..Default::default()
2365                                }]),
2366                                ..Default::default()
2367                            }]),
2368                            ..Default::default()
2369                        })
2370                            as Box<dyn MetaValue>)),
2371                        _ => None,
2372                    }
2373                })
2374            })),
2375        ));
2376
2377        let result = engine
2378            .evaluate_with_config("%test_variable.name.given", vec![], config)
2379            .await
2380            .unwrap();
2381
2382        let value = result.values[0]
2383            .as_any()
2384            .downcast_ref::<FHIRString>()
2385            .unwrap();
2386
2387        assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
2388    }
2389
2390    #[tokio::test]
2391    async fn test_external_constant_function_reference() {
2392        let engine = FPEngine::new();
2393
2394        let patient = Arc::new(Patient {
2395            name: Some(vec![HumanName {
2396                given: Some(vec![FHIRString {
2397                    value: Some("Paul".to_string()),
2398                    ..Default::default()
2399                }]),
2400                ..Default::default()
2401            }]),
2402            ..Default::default()
2403        });
2404
2405        let resolver = {
2406            let patient = patient.clone();
2407            ExternalConstantResolver::Function(Box::new(move |v| {
2408                let patient = patient.clone();
2409                Box::pin(async move {
2410                    // let patient = patient.clone();
2411                    match v.as_ref() {
2412                        "test_variable" => Some(ResolvedValue::Arc(patient.clone())),
2413                        _ => None,
2414                    }
2415                })
2416            }))
2417        };
2418
2419        let config = Arc::new(Config::builder().with_variable_resolver(resolver));
2420
2421        let result = engine
2422            .evaluate_with_config("%test_variable.name.given", vec![], config)
2423            .await
2424            .unwrap();
2425
2426        let value = result.values[0]
2427            .as_any()
2428            .downcast_ref::<FHIRString>()
2429            .unwrap();
2430
2431        assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
2432    }
2433
2434    #[tokio::test]
2435    async fn test_upper_function() {
2436        let engine = FPEngine::new();
2437
2438        let result = engine.evaluate("'hello'.upper()", vec![]).await.unwrap();
2439        assert_eq!(result.values.len(), 1);
2440        let value = result.values[0]
2441            .as_any()
2442            .downcast_ref::<FHIRString>()
2443            .unwrap();
2444        assert_eq!(value.value.as_deref(), Some("HELLO"));
2445
2446        let result = engine.evaluate("'AbCd'.upper()", vec![]).await.unwrap();
2447        let value = result.values[0]
2448            .as_any()
2449            .downcast_ref::<FHIRString>()
2450            .unwrap();
2451        assert_eq!(value.value.as_deref(), Some("ABCD"));
2452
2453        let result = engine.evaluate("'XYZ'.upper()", vec![]).await.unwrap();
2454        let value = result.values[0]
2455            .as_any()
2456            .downcast_ref::<FHIRString>()
2457            .unwrap();
2458        assert_eq!(value.value.as_deref(), Some("XYZ"));
2459    }
2460
2461    #[tokio::test]
2462    async fn test_lower_function() {
2463        let engine = FPEngine::new();
2464
2465        let result = engine.evaluate("'HELLO'.lower()", vec![]).await.unwrap();
2466        assert_eq!(result.values.len(), 1);
2467        let value = result.values[0]
2468            .as_any()
2469            .downcast_ref::<FHIRString>()
2470            .unwrap();
2471        assert_eq!(value.value.as_deref(), Some("hello"));
2472
2473        let result = engine.evaluate("'AbCd'.lower()", vec![]).await.unwrap();
2474        let value = result.values[0]
2475            .as_any()
2476            .downcast_ref::<FHIRString>()
2477            .unwrap();
2478        assert_eq!(value.value.as_deref(), Some("abcd"));
2479
2480        let result = engine.evaluate("'xyz'.lower()", vec![]).await.unwrap();
2481        let value = result.values[0]
2482            .as_any()
2483            .downcast_ref::<FHIRString>()
2484            .unwrap();
2485        assert_eq!(value.value.as_deref(), Some("xyz"));
2486    }
2487
2488    #[tokio::test]
2489    async fn get_resource_key() {
2490        let engine = FPEngine::new();
2491        let fp_config = Config::builder().with_resource_id("asdf".to_string());
2492
2493        let result = engine
2494            .evaluate_with_config("getResourceKey()", vec![], Arc::new(fp_config))
2495            .await
2496            .unwrap();
2497
2498        let k = result.iter().collect::<Vec<_>>();
2499
2500        assert_eq!(k.len(), 1);
2501
2502        let s = k[0].as_any().downcast_ref::<FHIRId>().unwrap();
2503        assert_eq!(s.value.as_deref(), Some("asdf"));
2504    }
2505
2506    #[tokio::test]
2507    async fn get_reference_key() {
2508        let engine = FPEngine::new();
2509
2510        let group = Group {
2511            member: Some(vec![GroupMember {
2512                entity: Box::new(Reference {
2513                    reference: Some(Box::new("Patient/123".to_string().into())),
2514                    ..Default::default()
2515                }),
2516                ..Default::default()
2517            }]),
2518            ..Default::default()
2519        };
2520
2521        let result = engine
2522            .evaluate("$this.member.entity.getReferenceKey(Patient)", vec![&group])
2523            .await
2524            .expect("Failed to evaluate getReferenceKey");
2525
2526        let ids = result.iter().collect::<Vec<_>>();
2527
2528        assert_eq!(ids.len(), 1);
2529
2530        let s = ids[0].as_any().downcast_ref::<FHIRId>().unwrap();
2531        assert_eq!(s.value.as_deref(), Some("123"));
2532
2533        let result = engine
2534            .evaluate("$this.member.entity.getReferenceKey(Group)", vec![&group])
2535            .await
2536            .expect("Failed to evaluate getReferenceKey");
2537
2538        let ids = result.iter().collect::<Vec<_>>();
2539        assert_eq!(ids.len(), 0);
2540
2541        let result = engine
2542            .evaluate("$this.member.entity.getReferenceKey()", vec![&group])
2543            .await
2544            .expect("Failed to evaluate getReferenceKey");
2545
2546        let ids = result.iter().collect::<Vec<_>>();
2547        assert_eq!(ids.len(), 1);
2548        let s = ids[0].as_any().downcast_ref::<FHIRId>().unwrap();
2549        assert_eq!(s.value.as_deref(), Some("123"));
2550    }
2551
2552    #[tokio::test]
2553    async fn exists_with_clause() {
2554        let patient = Patient {
2555            name: Some(vec![
2556                HumanName {
2557                    given: Some(vec![FHIRString {
2558                        value: Some("Alice".to_string()),
2559                        ..Default::default()
2560                    }]),
2561                    ..Default::default()
2562                },
2563                HumanName {
2564                    given: Some(vec![FHIRString {
2565                        value: Some("Matilda".to_string()),
2566                        ..Default::default()
2567                    }]),
2568                    ..Default::default()
2569                },
2570            ]),
2571            ..Default::default()
2572        };
2573
2574        let engine = FPEngine::new();
2575        let result = engine
2576            .evaluate("$this.name.exists(given.exists())", vec![&patient])
2577            .await
2578            .unwrap();
2579
2580        let result = result.iter().collect::<Vec<_>>();
2581
2582        assert_eq!(result.len(), 1);
2583        let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2584        assert_eq!(s.value, Some(true));
2585
2586        let result = engine
2587            .evaluate("$this.name.exists(given.empty())", vec![&patient])
2588            .await
2589            .unwrap();
2590
2591        let result = result.iter().collect::<Vec<_>>();
2592        assert_eq!(result.len(), 1);
2593        let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2594        assert_eq!(s.value, Some(false));
2595
2596        let result = engine
2597            .evaluate("$this.name.exists(given = 'Matilda')", vec![&patient])
2598            .await
2599            .unwrap();
2600
2601        let result = result.iter().collect::<Vec<_>>();
2602        assert_eq!(result.len(), 1);
2603        let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2604        assert_eq!(s.value, Some(true));
2605
2606        let result = engine
2607            .evaluate("$this.name.exists(given = 'Jane')", vec![&patient])
2608            .await
2609            .unwrap();
2610
2611        let result = result.iter().collect::<Vec<_>>();
2612        assert_eq!(result.len(), 1);
2613        let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2614        assert_eq!(s.value, Some(false));
2615    }
2616    #[tokio::test]
2617    async fn test_first() {
2618        let engine = FPEngine::new();
2619
2620        let group = Group {
2621            member: Some(vec![
2622                GroupMember {
2623                    entity: Box::new(Reference {
2624                        reference: Some(Box::new("Patient/1".to_string().into())),
2625                        ..Default::default()
2626                    }),
2627                    ..Default::default()
2628                },
2629                GroupMember {
2630                    entity: Box::new(Reference {
2631                        reference: Some(Box::new("Patient/2".to_string().into())),
2632                        ..Default::default()
2633                    }),
2634                    ..Default::default()
2635                },
2636            ]),
2637            ..Default::default()
2638        };
2639
2640        let result = engine
2641            .evaluate("$this.member.entity.first()", vec![&group])
2642            .await
2643            .expect("Failed to evaluate first()");
2644
2645        let references = result.iter().collect::<Vec<_>>();
2646        assert_eq!(references.len(), 1);
2647
2648        let s = references[0].as_any().downcast_ref::<Reference>().unwrap();
2649        assert_eq!(
2650            s.reference.as_ref().unwrap().value,
2651            Some("Patient/1".to_string())
2652        );
2653    }
2654
2655    #[tokio::test]
2656    async fn test_join() {
2657        let engine = FPEngine::new();
2658
2659        let mut patient = test_patient();
2660        patient.name.as_mut().unwrap()[0]
2661            .given
2662            .as_mut()
2663            .unwrap()
2664            .push(FHIRString {
2665                value: Some("David".to_string()),
2666                ..Default::default()
2667            });
2668
2669        let result = engine
2670            .evaluate("$this.name.given.join(',')", vec![&patient])
2671            .await
2672            .expect("Failed to evaluate join()");
2673
2674        let joined_ = result.iter().collect::<Vec<_>>();
2675        assert_eq!(joined_.len(), 1);
2676        let s = joined_[0].as_any().downcast_ref::<FHIRString>().unwrap();
2677        assert_eq!(s.value, Some("Bob,David".to_string()));
2678
2679        let result = engine
2680            .evaluate("$this.name.given.join()", vec![&patient])
2681            .await
2682            .expect("Failed to evaluate join()");
2683
2684        let joined_ = result.iter().collect::<Vec<_>>();
2685        assert_eq!(joined_.len(), 1);
2686        let s = joined_[0].as_any().downcast_ref::<FHIRString>().unwrap();
2687        assert_eq!(s.value, Some("BobDavid".to_string()));
2688
2689        let result = engine.evaluate("$this.name.join()", vec![&patient]).await;
2690
2691        assert_eq!(result.is_err(), true);
2692    }
2693
2694    #[tokio::test]
2695    async fn numerical_comparisons() {
2696        let engine = FPEngine::new();
2697
2698        let result = engine
2699            .evaluate("5 > 5", vec![])
2700            .await
2701            .expect("Failed to evaluate join()");
2702
2703        assert_eq!(result.values.len(), 1);
2704        let b = result.values[0]
2705            .as_any()
2706            .downcast_ref::<FHIRBoolean>()
2707            .unwrap();
2708        assert_eq!(b.value, Some(false));
2709
2710        let result = engine
2711            .evaluate("5 > 4", vec![])
2712            .await
2713            .expect("Failed to evaluate join()");
2714
2715        assert_eq!(result.values.len(), 1);
2716        let b = result.values[0]
2717            .as_any()
2718            .downcast_ref::<FHIRBoolean>()
2719            .unwrap();
2720        assert_eq!(b.value, Some(true));
2721
2722        let result = engine
2723            .evaluate("5 >= 5", vec![])
2724            .await
2725            .expect("Failed to evaluate join()");
2726
2727        assert_eq!(result.values.len(), 1);
2728        let b = result.values[0]
2729            .as_any()
2730            .downcast_ref::<FHIRBoolean>()
2731            .unwrap();
2732        assert_eq!(b.value, Some(true));
2733
2734        let result = engine
2735            .evaluate("4 >= 5", vec![])
2736            .await
2737            .expect("Failed to evaluate join()");
2738
2739        assert_eq!(result.values.len(), 1);
2740        let b = result.values[0]
2741            .as_any()
2742            .downcast_ref::<FHIRBoolean>()
2743            .unwrap();
2744        assert_eq!(b.value, Some(false));
2745
2746        let result = engine
2747            .evaluate("6 <= 5", vec![])
2748            .await
2749            .expect("Failed to evaluate join()");
2750
2751        assert_eq!(result.values.len(), 1);
2752        let b = result.values[0]
2753            .as_any()
2754            .downcast_ref::<FHIRBoolean>()
2755            .unwrap();
2756        assert_eq!(b.value, Some(false));
2757
2758        let result = engine
2759            .evaluate("6 <= 6", vec![])
2760            .await
2761            .expect("Failed to evaluate join()");
2762
2763        assert_eq!(result.values.len(), 1);
2764        let b = result.values[0]
2765            .as_any()
2766            .downcast_ref::<FHIRBoolean>()
2767            .unwrap();
2768        assert_eq!(b.value, Some(true));
2769
2770        let result = engine
2771            .evaluate("6 < 6", vec![])
2772            .await
2773            .expect("Failed to evaluate join()");
2774
2775        assert_eq!(result.values.len(), 1);
2776        let b = result.values[0]
2777            .as_any()
2778            .downcast_ref::<FHIRBoolean>()
2779            .unwrap();
2780        assert_eq!(b.value, Some(false));
2781
2782        let result = engine
2783            .evaluate("6 < 7", vec![])
2784            .await
2785            .expect("Failed to evaluate join()");
2786
2787        assert_eq!(result.values.len(), 1);
2788        let b = result.values[0]
2789            .as_any()
2790            .downcast_ref::<FHIRBoolean>()
2791            .unwrap();
2792        assert_eq!(b.value, Some(true));
2793    }
2794}