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