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
1244    #[must_use]
1245    pub fn iter(&'a self) -> Box<dyn Iterator<Item = &'a dyn MetaValue> + 'a> {
1246        Box::new(self.values.iter().copied())
1247    }
1248}
1249
1250impl Clone for Context<'_> {
1251    fn clone(&self) -> Self {
1252        Self {
1253            allocator: self.allocator.clone(),
1254            values: self.values.clone(),
1255        }
1256    }
1257}
1258
1259pub struct FPEngine {}
1260
1261static AST: LazyLock<DashMap<String, Arc<Expression>>> = LazyLock::new(DashMap::new);
1262
1263fn get_ast(path: &str) -> Result<Arc<Expression>, FHIRPathError> {
1264    if let Some(expression_ast) = AST.get(path) {
1265        return Ok(expression_ast.clone());
1266    }
1267
1268    let expression_ast = Arc::new(parser::parse(path)?);
1269    AST.insert(path.to_string(), expression_ast.clone());
1270
1271    Ok(expression_ast)
1272}
1273
1274impl Default for FPEngine {
1275    fn default() -> Self {
1276        Self::new()
1277    }
1278}
1279
1280impl FPEngine {
1281    #[must_use]
1282    pub fn new() -> Self {
1283        Self {}
1284    }
1285
1286    /// Evaluate a `FHIRPath` expression against a context.
1287    /// The context is a vector of references to `MetaValue` objects.
1288    /// The path is a `FHIRPath` expression.
1289    /// The result is a vector of references to `MetaValue` objects.
1290    ///
1291    /// # Errors
1292    ///
1293    /// Returns a [`FHIRPathError`] if the expression parsing fails (bad AST)
1294    /// or if the evaluation engine encounters an error during execution.
1295    pub async fn evaluate<'a, 'b>(
1296        &self,
1297        path: &str,
1298        values: Vec<&'a dyn MetaValue>,
1299    ) -> Result<Context<'b>, FHIRPathError>
1300    where
1301        'a: 'b,
1302    {
1303        let ast = get_ast(path)?;
1304
1305        // Store created.
1306        let allocator: Arc<Mutex<allocators::bumpalo::Allocator>> =
1307            Arc::new(Mutex::new(allocators::bumpalo::Allocator::new()));
1308
1309        let context = Context::new(values, allocator.clone());
1310
1311        let result = evaluate_expression(&ast, context, None).await?;
1312        Ok(result)
1313    }
1314
1315    /// Evaluate a `FHIRPath` expression against a context.
1316    /// The context is a vector of references to `MetaValue` objects.
1317    /// The path is a `FHIRPath` expression.
1318    /// The result is a vector of references to `MetaValue` objects.
1319    ///
1320    /// # Errors
1321    ///
1322    /// Returns a [`FHIRPathError`] if the expression parsing fails (bad AST)
1323    /// or if the evaluation engine encounters an error during execution.
1324    pub async fn evaluate_with_config<'a, 'b>(
1325        &self,
1326        path: &str,
1327        values: Vec<&'a dyn MetaValue>,
1328        config: Arc<Config<'b>>,
1329    ) -> Result<Context<'b>, FHIRPathError>
1330    where
1331        'a: 'b,
1332    {
1333        let ast = get_ast(path)?;
1334
1335        // Store created.
1336        let allocator = Arc::new(Mutex::new(allocators::bumpalo::Allocator::new()));
1337
1338        let context = Context::new(values, allocator.clone());
1339
1340        let result = evaluate_expression(&ast, context, Some(config)).await?;
1341
1342        Ok(result)
1343    }
1344}
1345
1346#[cfg(test)]
1347mod tests {
1348    use super::*;
1349    use haste_fhir_model::r4::{
1350        datetime::DateTime,
1351        generated::{
1352            resources::{
1353                Bundle, Group, GroupMember, Patient, PatientDeceasedTypeChoice, PatientLink,
1354                Resource, SearchParameter,
1355            },
1356            types::{
1357                Extension, ExtensionValueTypeChoice, FHIRDateTime, FHIRString, FHIRUri, HumanName,
1358                Identifier, Reference,
1359            },
1360        },
1361    };
1362
1363    use haste_reflect_derive::Reflect;
1364
1365    #[derive(Reflect, Debug)]
1366    #[fhir_type = "BackboneElement"]
1367    struct C {
1368        c: String,
1369    }
1370
1371    #[derive(Reflect, Debug)]
1372    #[fhir_type = "BackboneElement"]
1373    struct B {
1374        b: Vec<C>,
1375    }
1376
1377    #[derive(Reflect, Debug)]
1378    #[fhir_type = "BackboneElement"]
1379    struct A {
1380        a: Vec<B>,
1381    }
1382
1383    fn load_search_parameters() -> Vec<SearchParameter> {
1384        let json = include_str!(
1385            "../../../../artifacts/r4/hl7-core/definitions/hl7/search-parameters.min.json"
1386        );
1387        let bundle = serde_json::from_str::<Bundle>(json).unwrap();
1388
1389        bundle
1390            .entry
1391            .unwrap_or_default()
1392            .into_iter()
1393            .filter_map(|e| e.resource)
1394            .filter_map(|e| match *e {
1395                Resource::SearchParameter(sp) => Some(sp),
1396                _ => None,
1397            })
1398            .collect()
1399    }
1400
1401    #[tokio::test]
1402    async fn filter_typechoice_test() {
1403        let patient = Patient {
1404            id: Some("patient-id".to_string()),
1405            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
1406                value: Some(true),
1407                ..Default::default()
1408            }))),
1409            ..Default::default()
1410        };
1411
1412        let engine = FPEngine::new();
1413        let result = engine
1414            .evaluate("(Patient.deceased.ofType(dateTime))", vec![&patient])
1415            .await
1416            .unwrap();
1417
1418        assert_eq!(result.values.len(), 0);
1419
1420        let result = engine
1421            .evaluate("(Patient.deceased.ofType(boolean))", vec![&patient])
1422            .await
1423            .unwrap();
1424
1425        let value = result.values[0];
1426        let boolean_value: &FHIRBoolean = value
1427            .as_any()
1428            .downcast_ref::<FHIRBoolean>()
1429            .expect("Failed to downcast to FHIRBoolean");
1430
1431        assert_eq!(boolean_value.value, Some(true));
1432
1433        let patient = Patient {
1434            id: Some("patient-id".to_string()),
1435            deceased: Some(PatientDeceasedTypeChoice::DateTime(Box::new(
1436                FHIRDateTime {
1437                    value: Some(DateTime::Year(1980)),
1438                    ..Default::default()
1439                },
1440            ))),
1441            ..Default::default()
1442        };
1443
1444        let result = engine
1445            .evaluate("(Patient.deceased.ofType(boolean))", vec![&patient])
1446            .await
1447            .unwrap();
1448
1449        assert_eq!(result.values.len(), 0);
1450
1451        let result = engine
1452            .evaluate("(Patient.deceased.ofType(dateTime))", vec![&patient])
1453            .await
1454            .unwrap();
1455
1456        assert_eq!(result.values.len(), 1);
1457
1458        let value = result.values[0];
1459        let datetime_value: &FHIRDateTime = value
1460            .as_any()
1461            .downcast_ref::<FHIRDateTime>()
1462            .expect("Failed to downcast to FHIRDateTime");
1463
1464        assert_eq!(datetime_value.value, Some(DateTime::Year(1980)));
1465    }
1466
1467    #[tokio::test]
1468    async fn test_variable_resolution() {
1469        let engine = FPEngine::new();
1470        let patient = Patient {
1471            id: Some("my-patient".to_string()),
1472            ..Default::default()
1473        };
1474        let config = Arc::new(
1475            Config::builder().with_variable_resolver(ExternalConstantResolver::Variable(Arc::new(
1476                vec![("patient".to_string(), &patient as &dyn MetaValue)]
1477                    .into_iter()
1478                    .collect(),
1479            ))),
1480        );
1481
1482        let result = engine
1483            .evaluate_with_config("%patient", vec![], config.clone())
1484            .await
1485            .unwrap();
1486
1487        assert_eq!(result.values.len(), 1);
1488        let p = result.values[0].as_any().downcast_ref::<Patient>().unwrap();
1489
1490        assert_eq!(p.id, patient.id);
1491
1492        let result_failed = engine
1493            .evaluate_with_config("%nobody", vec![], config)
1494            .await
1495            .unwrap();
1496
1497        assert_eq!(result_failed.values.len(), 0);
1498    }
1499
1500    #[tokio::test]
1501    async fn test_where_clause() {
1502        let engine = FPEngine::new();
1503        let mut patient = Patient::default();
1504        let mut identifier = Identifier::default();
1505        let extension = Extension {
1506            id: None,
1507            url: "test-extension".to_string(),
1508            extension: None,
1509            value: Some(ExtensionValueTypeChoice::String(Box::new(FHIRString {
1510                id: None,
1511                extension: None,
1512                value: Some("example value".to_string()),
1513            }))),
1514        };
1515        identifier.value = Some(Box::new(FHIRString {
1516            id: None,
1517            extension: Some(vec![extension]),
1518            value: Some("12345".to_string()),
1519        }));
1520        patient.identifier_ = Some(vec![identifier]);
1521
1522        let context = engine
1523            .evaluate(
1524                "$this.identifier.value.where($this.extension.value.exists())",
1525                vec![&patient],
1526            )
1527            .await;
1528
1529        assert_eq!(context.unwrap().values.len(), 1);
1530
1531        let context = engine
1532            .evaluate(
1533                "$this.identifier.value.where($this.extension.extension.exists())",
1534                vec![&patient],
1535            )
1536            .await;
1537        assert_eq!(context.unwrap().values.len(), 0);
1538    }
1539
1540    #[tokio::test]
1541    async fn test_all_parameters() {
1542        let search_parameters = load_search_parameters();
1543        for param in &search_parameters {
1544            if let Some(expression) = &param.expression {
1545                let engine = FPEngine::new();
1546                let context = engine
1547                    .evaluate(expression.value.as_ref().unwrap().as_str(), vec![])
1548                    .await;
1549
1550                if let Err(err) = context {
1551                    panic!(
1552                        "Failed to evaluate search parameter '{}': {}",
1553                        expression.value.as_ref().unwrap(),
1554                        err
1555                    );
1556                }
1557            }
1558        }
1559    }
1560
1561    fn test_patient() -> Patient {
1562        let mut patient = Patient::default();
1563        let name = HumanName {
1564            given: Some(vec![FHIRString {
1565                id: None,
1566                extension: None,
1567                value: Some("Bob".to_string()),
1568            }]),
1569            ..Default::default()
1570        };
1571        let mrn_identifier = Identifier {
1572            value: Some(Box::new(FHIRString {
1573                id: None,
1574                extension: None,
1575                value: Some("mrn-12345".to_string()),
1576            })),
1577            system: Some(Box::new(FHIRUri {
1578                id: None,
1579                extension: None,
1580                value: Some("mrn".to_string()),
1581            })),
1582            ..Default::default()
1583        };
1584        let ssn_identifier = Identifier {
1585            value: Some(Box::new(FHIRString {
1586                id: None,
1587                extension: None,
1588                value: Some("ssn-12345".to_string()),
1589            })),
1590            system: Some(Box::new(FHIRUri {
1591                id: None,
1592                extension: None,
1593                value: Some("ssn".to_string()),
1594            })),
1595            ..Default::default()
1596        };
1597
1598        patient.identifier_ = Some(vec![mrn_identifier, ssn_identifier]);
1599        patient.name = Some(vec![name]);
1600        patient
1601    }
1602
1603    #[tokio::test]
1604    async fn indexing_tests() {
1605        let engine = FPEngine::new();
1606        let patient = test_patient();
1607
1608        let given_name = engine
1609            .evaluate("$this.name.given[0]", vec![&patient])
1610            .await
1611            .unwrap();
1612
1613        assert_eq!(given_name.values.len(), 1);
1614        let value = given_name.values[0];
1615        let name: &FHIRString = value
1616            .as_any()
1617            .downcast_ref::<FHIRString>()
1618            .expect("Failed to downcast to FHIRString");
1619
1620        assert_eq!(name.value.as_deref(), Some("Bob"));
1621
1622        let ssn_identifier = engine
1623            .evaluate("$this.identifier[1]", vec![&patient])
1624            .await
1625            .unwrap();
1626
1627        assert_eq!(ssn_identifier.values.len(), 1);
1628        let value = ssn_identifier.values[0];
1629        let identifier: &Identifier = value
1630            .as_any()
1631            .downcast_ref::<Identifier>()
1632            .expect("Failed to downcast to Identifier");
1633
1634        assert_eq!(
1635            identifier.value.as_ref().unwrap().value.as_deref(),
1636            Some("ssn-12345")
1637        );
1638
1639        let all_identifiers = engine
1640            .evaluate("$this.identifier", vec![&patient])
1641            .await
1642            .unwrap();
1643        assert_eq!(all_identifiers.values.len(), 2);
1644    }
1645
1646    #[tokio::test]
1647    async fn where_testing() {
1648        let engine = FPEngine::new();
1649        let patient = test_patient();
1650
1651        let name_where_clause = engine
1652            .evaluate(
1653                "$this.name.given.where($this.value = 'Bob')",
1654                vec![&patient],
1655            )
1656            .await
1657            .unwrap();
1658
1659        assert_eq!(name_where_clause.values.len(), 1);
1660        let value = name_where_clause.values[0];
1661        let name: &FHIRString = value
1662            .as_any()
1663            .downcast_ref::<FHIRString>()
1664            .expect("Failed to downcast to FHIRString");
1665
1666        assert_eq!(name.value.as_deref(), Some("Bob"));
1667
1668        let ssn_identifier_clause = engine
1669            .evaluate(
1670                "$this.identifier.where($this.system.value = 'ssn')",
1671                vec![&patient],
1672            )
1673            .await
1674            .unwrap();
1675        assert_eq!(ssn_identifier_clause.values.len(), 1);
1676
1677        let ssn_identifier = ssn_identifier_clause.values[0]
1678            .as_any()
1679            .downcast_ref::<Identifier>()
1680            .expect("Failed to downcast to Identifier");
1681
1682        assert_eq!(
1683            ssn_identifier.value.as_ref().unwrap().value.as_deref(),
1684            Some("ssn-12345")
1685        );
1686    }
1687
1688    #[tokio::test]
1689    async fn test_equality() {
1690        let engine = FPEngine::new();
1691
1692        // String tests
1693        let string_equal = engine.evaluate("'test' = 'test'", vec![]).await.unwrap();
1694        for r in &string_equal {
1695            let b = r
1696                .as_any()
1697                .downcast_ref::<FHIRBoolean>()
1698                .unwrap()
1699                .value
1700                .unwrap();
1701            assert!(b);
1702        }
1703        let string_unequal = engine.evaluate("'invalid' = 'test'", vec![]).await.unwrap();
1704        for r in &string_unequal {
1705            let b = r
1706                .as_any()
1707                .downcast_ref::<FHIRBoolean>()
1708                .unwrap()
1709                .value
1710                .unwrap();
1711            assert!(!b);
1712        }
1713
1714        // Number tests
1715        let number_equal = engine.evaluate("12 = 12", vec![]).await.unwrap();
1716        for r in &number_equal {
1717            let b = r
1718                .as_any()
1719                .downcast_ref::<FHIRBoolean>()
1720                .unwrap()
1721                .value
1722                .unwrap();
1723            assert!(b);
1724        }
1725        let number_unequal = engine.evaluate("13 = 12", vec![]).await.unwrap();
1726        for r in &number_unequal {
1727            let b = r
1728                .as_any()
1729                .downcast_ref::<FHIRBoolean>()
1730                .unwrap()
1731                .value
1732                .unwrap();
1733            assert!(!b);
1734        }
1735
1736        // Boolean tests
1737        let bool_equal = engine.evaluate("false = false", vec![]).await.unwrap();
1738        for r in &bool_equal {
1739            let b = r
1740                .as_any()
1741                .downcast_ref::<FHIRBoolean>()
1742                .unwrap()
1743                .value
1744                .unwrap();
1745            assert!(b);
1746        }
1747        let bool_unequal = engine.evaluate("false = true", vec![]).await.unwrap();
1748        for r in &bool_unequal {
1749            let b = r
1750                .as_any()
1751                .downcast_ref::<FHIRBoolean>()
1752                .unwrap()
1753                .value
1754                .unwrap();
1755            assert!(!b);
1756        }
1757
1758        // Nested Equality tests
1759        let bool_equal = engine.evaluate("12 = 13 = false", vec![]).await.unwrap();
1760        for r in &bool_equal {
1761            let b = r
1762                .as_any()
1763                .downcast_ref::<FHIRBoolean>()
1764                .unwrap()
1765                .value
1766                .unwrap();
1767            assert!(b);
1768        }
1769        let bool_unequal = engine.evaluate("12 = 13 = true", vec![]).await.unwrap();
1770        for r in &bool_unequal {
1771            let b = r
1772                .as_any()
1773                .downcast_ref::<FHIRBoolean>()
1774                .unwrap()
1775                .value
1776                .unwrap();
1777            assert!(!b);
1778        }
1779        let bool_unequal = engine.evaluate("12 = (13 - 1)", vec![]).await.unwrap();
1780        for r in &bool_unequal {
1781            let b = r
1782                .as_any()
1783                .downcast_ref::<FHIRBoolean>()
1784                .unwrap()
1785                .value
1786                .unwrap();
1787            assert!(b);
1788        }
1789    }
1790
1791    #[tokio::test]
1792    async fn test_string_concat() {
1793        let engine = FPEngine::new();
1794        let patient = test_patient();
1795
1796        let simple_result = engine.evaluate("'Hello' + ' World'", vec![]).await.unwrap();
1797        for r in &simple_result {
1798            let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1799            assert_eq!(s.value, Some("Hello World".to_string()));
1800        }
1801
1802        let simple_result = engine
1803            .evaluate("$this.name.given + ' Miller'", vec![&patient])
1804            .await
1805            .unwrap();
1806        for r in &simple_result {
1807            let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1808            assert_eq!(s.value, Some("Bob Miller".to_string()));
1809        }
1810    }
1811
1812    #[tokio::test]
1813    async fn test_simple() {
1814        let root = A {
1815            a: vec![B {
1816                b: vec![C {
1817                    c: "whatever".to_string(),
1818                }],
1819            }],
1820        };
1821
1822        let engine = FPEngine::new();
1823        let result = engine.evaluate("a.b.c", vec![&root]).await.unwrap();
1824
1825        let strings: Vec<&String> = result
1826            .iter()
1827            .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1828            .collect();
1829
1830        assert_eq!(strings, vec!["whatever"]);
1831    }
1832
1833    #[tokio::test]
1834    async fn allocation() {
1835        let engine = FPEngine::new();
1836        let result = engine.evaluate("'asdf'", vec![]).await.unwrap();
1837
1838        for r in &result {
1839            let s = r.as_any().downcast_ref::<FHIRString>().unwrap().clone();
1840
1841            assert_eq!(s.value, Some("asdf".to_string()));
1842        }
1843    }
1844
1845    #[tokio::test]
1846    async fn order_operation() {
1847        let engine = FPEngine::new();
1848        let result = engine.evaluate("45 + 2  * 3", vec![]).await.unwrap();
1849
1850        for r in &result {
1851            let s = r.as_any().downcast_ref::<FHIRDecimal>().unwrap().clone();
1852
1853            assert_eq!(s.value, Some(51.0));
1854        }
1855    }
1856
1857    #[tokio::test]
1858    async fn xor_operation() {
1859        let engine = FPEngine::new();
1860        let result = engine.evaluate("true xor true", vec![]).await.unwrap();
1861
1862        for r in &result {
1863            let b = r
1864                .as_any()
1865                .downcast_ref::<FHIRBoolean>()
1866                .unwrap()
1867                .value
1868                .unwrap();
1869
1870            assert!(!b);
1871        }
1872    }
1873
1874    #[tokio::test]
1875    async fn domain_resource_filter() {
1876        let engine = FPEngine::new();
1877
1878        let patient =
1879            serde_json::from_str::<Resource>(r#"{"id": "patient-id", "resourceType": "Patient"}"#)
1880                .unwrap();
1881        let result = engine
1882            .evaluate("Resource.id", vec![&patient])
1883            .await
1884            .unwrap();
1885        let ids: Vec<&String> = result
1886            .iter()
1887            .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1888            .collect();
1889
1890        assert_eq!(ids.len(), 1);
1891        assert_eq!(ids[0], "patient-id");
1892
1893        let result2 = engine
1894            .evaluate("DomainResource.id", vec![&patient])
1895            .await
1896            .unwrap();
1897        let ids2: Vec<&String> = result2
1898            .iter()
1899            .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1900            .collect();
1901        assert_eq!(ids2.len(), 1);
1902        assert_eq!(ids2[0], "patient-id");
1903    }
1904
1905    #[tokio::test]
1906    async fn type_test() {
1907        let engine = FPEngine::new();
1908        let patient = Patient::default();
1909
1910        let result = engine
1911            .evaluate("$this.type().name", vec![&patient])
1912            .await
1913            .unwrap();
1914        let ids: Vec<&String> = result
1915            .iter()
1916            .map(|r| r.as_any().downcast_ref::<String>().unwrap())
1917            .collect();
1918
1919        assert_eq!(ids.len(), 1);
1920        assert_eq!(ids[0], "Patient");
1921    }
1922
1923    #[tokio::test]
1924    async fn resolve_test() {
1925        let engine = FPEngine::new();
1926        let observation = serde_json::from_str::<Resource>(r#"
1927             {
1928                "resourceType": "Observation",
1929                "id": "f001",
1930                "text": {
1931                    "status": "generated",
1932                    "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>"
1933                },
1934                "identifier": [
1935                    {
1936                    "use": "official",
1937                    "system": "http://www.bmc.nl/zorgportal/identifiers/observations",
1938                    "value": "6323"
1939                    }
1940                ],
1941                "status": "final",
1942                "code": {
1943                    "coding": [
1944                    {
1945                        "system": "http://loinc.org",
1946                        "code": "15074-8",
1947                        "display": "Glucose [Moles/volume] in Blood"
1948                    }
1949                    ]
1950                },
1951                "subject": {
1952                    "reference": "Patient/f001",
1953                    "display": "P. van de Heuvel"
1954                },
1955                "effectivePeriod": {
1956                    "start": "2013-04-02T09:30:10+01:00"
1957                },
1958                "issued": "2013-04-03T15:30:10+01:00",
1959                "performer": [
1960                    {
1961                    "reference": "Practitioner/f005",
1962                    "display": "A. Langeveld"
1963                    }
1964                ],
1965                "valueQuantity": {
1966                    "value": 6.3,
1967                    "unit": "mmol/l",
1968                    "system": "http://unitsofmeasure.org",
1969                    "code": "mmol/L"
1970                },
1971                "interpretation": [
1972                    {
1973                    "coding": [
1974                        {
1975                        "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
1976                        "code": "H",
1977                        "display": "High"
1978                        }
1979                    ]
1980                    }
1981                ],
1982                "referenceRange": [
1983                    {
1984                    "low": {
1985                        "value": 3.1,
1986                        "unit": "mmol/l",
1987                        "system": "http://unitsofmeasure.org",
1988                        "code": "mmol/L"
1989                    },
1990                    "high": {
1991                        "value": 6.2,
1992                        "unit": "mmol/l",
1993                        "system": "http://unitsofmeasure.org",
1994                        "code": "mmol/L"
1995                    }
1996                    }
1997                ]
1998                }
1999            "#).unwrap();
2000
2001        let result = engine
2002            .evaluate(
2003                "Observation.subject.where(resolve() is Patient)",
2004                vec![&observation],
2005            )
2006            .await
2007            .unwrap();
2008
2009        let references: Vec<&Reference> = result
2010            .iter()
2011            .map(|r| r.as_any().downcast_ref::<Reference>().unwrap())
2012            .collect();
2013
2014        assert_eq!(references.len(), 1);
2015        assert_eq!(
2016            references[0].reference.as_ref().unwrap().value,
2017            Some("Patient/f001".to_string())
2018        );
2019    }
2020
2021    #[tokio::test]
2022    async fn children_test() {
2023        let engine = FPEngine::new();
2024        let patient = Patient {
2025            name: Some(vec![HumanName {
2026                given: Some(vec![FHIRString {
2027                    value: Some("Alice".to_string()),
2028                    ..Default::default()
2029                }]),
2030                ..Default::default()
2031            }]),
2032            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2033                value: Some(true),
2034                ..Default::default()
2035            }))),
2036            ..Default::default()
2037        };
2038
2039        let result = engine
2040            .evaluate("$this.children()", vec![&patient])
2041            .await
2042            .unwrap();
2043
2044        assert_eq!(result.values.len(), 2);
2045        assert_eq!(
2046            result
2047                .values
2048                .iter()
2049                .map(|v| v.fhir_type())
2050                .collect::<Vec<_>>(),
2051            vec!["HumanName", "boolean"]
2052        );
2053    }
2054
2055    #[tokio::test]
2056    async fn repeat_test() {
2057        let engine = FPEngine::new();
2058        let patient = Patient {
2059            name: Some(vec![HumanName {
2060                given: Some(vec![FHIRString {
2061                    value: Some("Alice".to_string()),
2062                    ..Default::default()
2063                }]),
2064                ..Default::default()
2065            }]),
2066            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2067                value: Some(true),
2068                ..Default::default()
2069            }))),
2070            ..Default::default()
2071        };
2072
2073        let result = engine
2074            .evaluate("$this.name.given", vec![&patient])
2075            .await
2076            .unwrap();
2077
2078        assert_eq!(result.values.len(), 1);
2079
2080        assert_eq!(result.values[0].fhir_type(), "string");
2081
2082        let result = engine
2083            .evaluate("$this.repeat(children())", vec![&patient])
2084            .await
2085            .unwrap();
2086
2087        assert_eq!(
2088            result
2089                .values
2090                .iter()
2091                .map(|v| v.fhir_type())
2092                .collect::<Vec<_>>(),
2093            vec![
2094                "HumanName",
2095                "boolean",
2096                "string",
2097                "http://hl7.org/fhirpath/System.Boolean",
2098                "http://hl7.org/fhirpath/System.String"
2099            ]
2100        );
2101    }
2102    #[tokio::test]
2103    async fn descendants_test() {
2104        let engine = FPEngine::new();
2105        let patient = Patient {
2106            name: Some(vec![HumanName {
2107                given: Some(vec![FHIRString {
2108                    value: Some("Alice".to_string()),
2109                    ..Default::default()
2110                }]),
2111                ..Default::default()
2112            }]),
2113            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2114                value: Some(true),
2115                ..Default::default()
2116            }))),
2117            ..Default::default()
2118        };
2119        let result = engine
2120            .evaluate("descendants()", vec![&patient])
2121            .await
2122            .unwrap();
2123
2124        assert_eq!(
2125            result
2126                .values
2127                .iter()
2128                .map(|v| v.fhir_type())
2129                .collect::<Vec<_>>(),
2130            vec![
2131                "HumanName",
2132                "boolean",
2133                "string",
2134                "http://hl7.org/fhirpath/System.Boolean",
2135                "http://hl7.org/fhirpath/System.String"
2136            ]
2137        );
2138    }
2139
2140    #[tokio::test]
2141    async fn descendants_test_filter() {
2142        let engine = FPEngine::new();
2143        let patient = Patient {
2144            link: Some(vec![PatientLink {
2145                other: Box::new(Reference {
2146                    reference: Some(Box::new(FHIRString {
2147                        value: Some("Patient/123".to_string()),
2148                        ..Default::default()
2149                    })),
2150                    ..Default::default()
2151                }),
2152                ..Default::default()
2153            }]),
2154            name: Some(vec![HumanName {
2155                given: Some(vec![FHIRString {
2156                    value: Some("Alice".to_string()),
2157                    ..Default::default()
2158                }]),
2159                ..Default::default()
2160            }]),
2161            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2162                value: Some(true),
2163                ..Default::default()
2164            }))),
2165            ..Default::default()
2166        };
2167        let result = engine
2168            .evaluate("descendants()", vec![&patient])
2169            .await
2170            .unwrap();
2171
2172        assert_eq!(
2173            result
2174                .values
2175                .iter()
2176                .map(|v| v.fhir_type())
2177                .collect::<Vec<_>>(),
2178            vec![
2179                "HumanName",
2180                "boolean",
2181                "BackboneElement",
2182                "string",
2183                "http://hl7.org/fhirpath/System.Boolean",
2184                "Reference",
2185                "http://hl7.org/fhirpath/System.String",
2186                "string",
2187                "http://hl7.org/fhirpath/System.String"
2188            ]
2189        );
2190
2191        let result = engine
2192            .evaluate("descendants().ofType(Reference)", vec![&patient])
2193            .await
2194            .unwrap();
2195
2196        assert_eq!(
2197            result
2198                .values
2199                .iter()
2200                .map(|v| v.fhir_type())
2201                .collect::<Vec<_>>(),
2202            vec!["Reference",]
2203        );
2204
2205        let value = result.values[0]
2206            .as_any()
2207            .downcast_ref::<Reference>()
2208            .unwrap();
2209
2210        assert_eq!(
2211            value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2212            "Patient/123"
2213        );
2214    }
2215
2216    #[tokio::test]
2217    async fn try_unsafe_set_from_ref() {
2218        let engine = FPEngine::new();
2219        let patient = Patient {
2220            link: Some(vec![PatientLink {
2221                other: Box::new(Reference {
2222                    reference: Some(Box::new(FHIRString {
2223                        value: Some("Patient/123".to_string()),
2224                        ..Default::default()
2225                    })),
2226                    ..Default::default()
2227                }),
2228                ..Default::default()
2229            }]),
2230            name: Some(vec![HumanName {
2231                given: Some(vec![FHIRString {
2232                    value: Some("Alice".to_string()),
2233                    ..Default::default()
2234                }]),
2235                ..Default::default()
2236            }]),
2237            deceased: Some(PatientDeceasedTypeChoice::Boolean(Box::new(FHIRBoolean {
2238                value: Some(true),
2239                ..Default::default()
2240            }))),
2241            ..Default::default()
2242        };
2243
2244        let result = engine
2245            .evaluate("descendants().ofType(Reference)", vec![&patient])
2246            .await
2247            .unwrap();
2248
2249        assert_eq!(
2250            result
2251                .values
2252                .iter()
2253                .map(|v| v.fhir_type())
2254                .collect::<Vec<_>>(),
2255            vec!["Reference",]
2256        );
2257
2258        let value = result.values[0]
2259            .as_any()
2260            .downcast_ref::<Reference>()
2261            .unwrap();
2262
2263        assert_eq!(
2264            value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2265            "Patient/123"
2266        );
2267
2268        // An example for use in transaction processing where we have a reference to an object
2269        // but need to modify it in place.
2270        unsafe {
2271            let r = std::ptr::from_ref::<Reference>(value);
2272            let mut_ptr = r.cast_mut();
2273
2274            (*mut_ptr).reference = Some(Box::new(FHIRString {
2275                value: Some("Patient/456".to_string()),
2276                ..Default::default()
2277            }));
2278        }
2279
2280        assert_eq!(
2281            value.reference.as_ref().unwrap().value.as_ref().unwrap(),
2282            "Patient/456"
2283        );
2284
2285        assert_eq!(
2286            patient.link.as_ref().unwrap()[0]
2287                .other
2288                .reference
2289                .as_ref()
2290                .unwrap()
2291                .value
2292                .as_ref()
2293                .unwrap(),
2294            "Patient/456"
2295        );
2296    }
2297
2298    #[tokio::test]
2299    async fn test_external_constant_function() {
2300        let engine = FPEngine::new();
2301
2302        let config = Arc::new(Config::builder().with_variable_resolver(
2303            ExternalConstantResolver::Function(Box::new(|v| {
2304                Box::pin(async move {
2305                    match v.as_ref() {
2306                        "test_variable" => Some(ResolvedValue::Box(Box::new(Patient {
2307                            name: Some(vec![HumanName {
2308                                given: Some(vec![FHIRString {
2309                                    value: Some("Paul".to_string()),
2310                                    ..Default::default()
2311                                }]),
2312                                ..Default::default()
2313                            }]),
2314                            ..Default::default()
2315                        })
2316                            as Box<dyn MetaValue>)),
2317                        _ => None,
2318                    }
2319                })
2320            })),
2321        ));
2322
2323        let result = engine
2324            .evaluate_with_config("%test_variable.name.given", vec![], config)
2325            .await
2326            .unwrap();
2327
2328        let value = result.values[0]
2329            .as_any()
2330            .downcast_ref::<FHIRString>()
2331            .unwrap();
2332
2333        assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
2334    }
2335
2336    #[tokio::test]
2337    async fn test_external_constant_function_reference() {
2338        let engine = FPEngine::new();
2339
2340        let patient = Arc::new(Patient {
2341            name: Some(vec![HumanName {
2342                given: Some(vec![FHIRString {
2343                    value: Some("Paul".to_string()),
2344                    ..Default::default()
2345                }]),
2346                ..Default::default()
2347            }]),
2348            ..Default::default()
2349        });
2350
2351        let resolver = {
2352            let patient = patient.clone();
2353            ExternalConstantResolver::Function(Box::new(move |v| {
2354                let patient = patient.clone();
2355                Box::pin(async move {
2356                    // let patient = patient.clone();
2357                    match v.as_ref() {
2358                        "test_variable" => Some(ResolvedValue::Arc(patient.clone())),
2359                        _ => None,
2360                    }
2361                })
2362            }))
2363        };
2364
2365        let config = Arc::new(Config::builder().with_variable_resolver(resolver));
2366
2367        let result = engine
2368            .evaluate_with_config("%test_variable.name.given", vec![], config)
2369            .await
2370            .unwrap();
2371
2372        let value = result.values[0]
2373            .as_any()
2374            .downcast_ref::<FHIRString>()
2375            .unwrap();
2376
2377        assert_eq!(value.value.as_ref(), Some(&"Paul".to_string()));
2378    }
2379
2380    #[tokio::test]
2381    async fn test_upper_function() {
2382        let engine = FPEngine::new();
2383
2384        let result = engine.evaluate("'hello'.upper()", vec![]).await.unwrap();
2385        assert_eq!(result.values.len(), 1);
2386        let value = result.values[0]
2387            .as_any()
2388            .downcast_ref::<FHIRString>()
2389            .unwrap();
2390        assert_eq!(value.value.as_deref(), Some("HELLO"));
2391
2392        let result = engine.evaluate("'AbCd'.upper()", vec![]).await.unwrap();
2393        let value = result.values[0]
2394            .as_any()
2395            .downcast_ref::<FHIRString>()
2396            .unwrap();
2397        assert_eq!(value.value.as_deref(), Some("ABCD"));
2398
2399        let result = engine.evaluate("'XYZ'.upper()", vec![]).await.unwrap();
2400        let value = result.values[0]
2401            .as_any()
2402            .downcast_ref::<FHIRString>()
2403            .unwrap();
2404        assert_eq!(value.value.as_deref(), Some("XYZ"));
2405    }
2406
2407    #[tokio::test]
2408    async fn test_lower_function() {
2409        let engine = FPEngine::new();
2410
2411        let result = engine.evaluate("'HELLO'.lower()", vec![]).await.unwrap();
2412        assert_eq!(result.values.len(), 1);
2413        let value = result.values[0]
2414            .as_any()
2415            .downcast_ref::<FHIRString>()
2416            .unwrap();
2417        assert_eq!(value.value.as_deref(), Some("hello"));
2418
2419        let result = engine.evaluate("'AbCd'.lower()", vec![]).await.unwrap();
2420        let value = result.values[0]
2421            .as_any()
2422            .downcast_ref::<FHIRString>()
2423            .unwrap();
2424        assert_eq!(value.value.as_deref(), Some("abcd"));
2425
2426        let result = engine.evaluate("'xyz'.lower()", vec![]).await.unwrap();
2427        let value = result.values[0]
2428            .as_any()
2429            .downcast_ref::<FHIRString>()
2430            .unwrap();
2431        assert_eq!(value.value.as_deref(), Some("xyz"));
2432    }
2433
2434    #[tokio::test]
2435    async fn get_resource_key() {
2436        let engine = FPEngine::new();
2437        let fp_config = Config::builder().with_resource_id("asdf".to_string());
2438
2439        let result = engine
2440            .evaluate_with_config("getResourceKey()", vec![], Arc::new(fp_config))
2441            .await
2442            .unwrap();
2443
2444        let k = result.iter().collect::<Vec<_>>();
2445
2446        assert_eq!(k.len(), 1);
2447
2448        let s = k[0].as_any().downcast_ref::<FHIRId>().unwrap();
2449        assert_eq!(s.value.as_deref(), Some("asdf"));
2450    }
2451
2452    #[tokio::test]
2453    async fn get_reference_key() {
2454        let engine = FPEngine::new();
2455
2456        let group = Group {
2457            member: Some(vec![GroupMember {
2458                entity: Box::new(Reference {
2459                    reference: Some(Box::new("Patient/123".to_string().into())),
2460                    ..Default::default()
2461                }),
2462                ..Default::default()
2463            }]),
2464            ..Default::default()
2465        };
2466
2467        let result = engine
2468            .evaluate("$this.member.entity.getReferenceKey(Patient)", vec![&group])
2469            .await
2470            .expect("Failed to evaluate getReferenceKey");
2471
2472        let ids = result.iter().collect::<Vec<_>>();
2473
2474        assert_eq!(ids.len(), 1);
2475
2476        let s = ids[0].as_any().downcast_ref::<FHIRId>().unwrap();
2477        assert_eq!(s.value.as_deref(), Some("123"));
2478
2479        let result = engine
2480            .evaluate("$this.member.entity.getReferenceKey(Group)", vec![&group])
2481            .await
2482            .expect("Failed to evaluate getReferenceKey");
2483
2484        let ids = result.iter().collect::<Vec<_>>();
2485        assert_eq!(ids.len(), 0);
2486
2487        let result = engine
2488            .evaluate("$this.member.entity.getReferenceKey()", vec![&group])
2489            .await
2490            .expect("Failed to evaluate getReferenceKey");
2491
2492        let ids = result.iter().collect::<Vec<_>>();
2493        assert_eq!(ids.len(), 1);
2494        let s = ids[0].as_any().downcast_ref::<FHIRId>().unwrap();
2495        assert_eq!(s.value.as_deref(), Some("123"));
2496    }
2497
2498    #[tokio::test]
2499    async fn exists_with_clause() {
2500        let patient = Patient {
2501            name: Some(vec![
2502                HumanName {
2503                    given: Some(vec![FHIRString {
2504                        value: Some("Alice".to_string()),
2505                        ..Default::default()
2506                    }]),
2507                    ..Default::default()
2508                },
2509                HumanName {
2510                    given: Some(vec![FHIRString {
2511                        value: Some("Matilda".to_string()),
2512                        ..Default::default()
2513                    }]),
2514                    ..Default::default()
2515                },
2516            ]),
2517            ..Default::default()
2518        };
2519
2520        let engine = FPEngine::new();
2521        let result = engine
2522            .evaluate("$this.name.exists(given.exists())", vec![&patient])
2523            .await
2524            .unwrap();
2525
2526        let result = result.iter().collect::<Vec<_>>();
2527
2528        assert_eq!(result.len(), 1);
2529        let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2530        assert_eq!(s.value, Some(true));
2531
2532        let result = engine
2533            .evaluate("$this.name.exists(given.empty())", vec![&patient])
2534            .await
2535            .unwrap();
2536
2537        let result = result.iter().collect::<Vec<_>>();
2538        assert_eq!(result.len(), 1);
2539        let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2540        assert_eq!(s.value, Some(false));
2541
2542        let result = engine
2543            .evaluate("$this.name.exists(given = 'Matilda')", vec![&patient])
2544            .await
2545            .unwrap();
2546
2547        let result = result.iter().collect::<Vec<_>>();
2548        assert_eq!(result.len(), 1);
2549        let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2550        assert_eq!(s.value, Some(true));
2551
2552        let result = engine
2553            .evaluate("$this.name.exists(given = 'Jane')", vec![&patient])
2554            .await
2555            .unwrap();
2556
2557        let result = result.iter().collect::<Vec<_>>();
2558        assert_eq!(result.len(), 1);
2559        let s = result[0].as_any().downcast_ref::<FHIRBoolean>().unwrap();
2560        assert_eq!(s.value, Some(false));
2561    }
2562    #[tokio::test]
2563    async fn test_first() {
2564        let engine = FPEngine::new();
2565
2566        let group = Group {
2567            member: Some(vec![
2568                GroupMember {
2569                    entity: Box::new(Reference {
2570                        reference: Some(Box::new("Patient/1".to_string().into())),
2571                        ..Default::default()
2572                    }),
2573                    ..Default::default()
2574                },
2575                GroupMember {
2576                    entity: Box::new(Reference {
2577                        reference: Some(Box::new("Patient/2".to_string().into())),
2578                        ..Default::default()
2579                    }),
2580                    ..Default::default()
2581                },
2582            ]),
2583            ..Default::default()
2584        };
2585
2586        let result = engine
2587            .evaluate("$this.member.entity.first()", vec![&group])
2588            .await
2589            .expect("Failed to evaluate first()");
2590
2591        let references = result.iter().collect::<Vec<_>>();
2592        assert_eq!(references.len(), 1);
2593
2594        let s = references[0].as_any().downcast_ref::<Reference>().unwrap();
2595        assert_eq!(
2596            s.reference.as_ref().unwrap().value,
2597            Some("Patient/1".to_string())
2598        );
2599    }
2600
2601    #[tokio::test]
2602    async fn test_join() {
2603        let engine = FPEngine::new();
2604
2605        let mut patient = test_patient();
2606        patient.name.as_mut().unwrap()[0]
2607            .given
2608            .as_mut()
2609            .unwrap()
2610            .push(FHIRString {
2611                value: Some("David".to_string()),
2612                ..Default::default()
2613            });
2614
2615        let result = engine
2616            .evaluate("$this.name.given.join(',')", vec![&patient])
2617            .await
2618            .expect("Failed to evaluate join()");
2619
2620        let joined_ = result.iter().collect::<Vec<_>>();
2621        assert_eq!(joined_.len(), 1);
2622        let s = joined_[0].as_any().downcast_ref::<FHIRString>().unwrap();
2623        assert_eq!(s.value, Some("Bob,David".to_string()));
2624
2625        let result = engine
2626            .evaluate("$this.name.given.join()", vec![&patient])
2627            .await
2628            .expect("Failed to evaluate join()");
2629
2630        let joined_ = result.iter().collect::<Vec<_>>();
2631        assert_eq!(joined_.len(), 1);
2632        let s = joined_[0].as_any().downcast_ref::<FHIRString>().unwrap();
2633        assert_eq!(s.value, Some("BobDavid".to_string()));
2634
2635        let result = engine.evaluate("$this.name.join()", vec![&patient]).await;
2636
2637        assert!(result.is_err());
2638    }
2639
2640    #[tokio::test]
2641    async fn numerical_comparisons() {
2642        let engine = FPEngine::new();
2643
2644        let result = engine
2645            .evaluate("5 > 5", vec![])
2646            .await
2647            .expect("Failed to evaluate join()");
2648
2649        assert_eq!(result.values.len(), 1);
2650        let b = result.values[0]
2651            .as_any()
2652            .downcast_ref::<FHIRBoolean>()
2653            .unwrap();
2654        assert_eq!(b.value, Some(false));
2655
2656        let result = engine
2657            .evaluate("5 > 4", vec![])
2658            .await
2659            .expect("Failed to evaluate join()");
2660
2661        assert_eq!(result.values.len(), 1);
2662        let b = result.values[0]
2663            .as_any()
2664            .downcast_ref::<FHIRBoolean>()
2665            .unwrap();
2666        assert_eq!(b.value, Some(true));
2667
2668        let result = engine
2669            .evaluate("5 >= 5", vec![])
2670            .await
2671            .expect("Failed to evaluate join()");
2672
2673        assert_eq!(result.values.len(), 1);
2674        let b = result.values[0]
2675            .as_any()
2676            .downcast_ref::<FHIRBoolean>()
2677            .unwrap();
2678        assert_eq!(b.value, Some(true));
2679
2680        let result = engine
2681            .evaluate("4 >= 5", vec![])
2682            .await
2683            .expect("Failed to evaluate join()");
2684
2685        assert_eq!(result.values.len(), 1);
2686        let b = result.values[0]
2687            .as_any()
2688            .downcast_ref::<FHIRBoolean>()
2689            .unwrap();
2690        assert_eq!(b.value, Some(false));
2691
2692        let result = engine
2693            .evaluate("6 <= 5", vec![])
2694            .await
2695            .expect("Failed to evaluate join()");
2696
2697        assert_eq!(result.values.len(), 1);
2698        let b = result.values[0]
2699            .as_any()
2700            .downcast_ref::<FHIRBoolean>()
2701            .unwrap();
2702        assert_eq!(b.value, Some(false));
2703
2704        let result = engine
2705            .evaluate("6 <= 6", vec![])
2706            .await
2707            .expect("Failed to evaluate join()");
2708
2709        assert_eq!(result.values.len(), 1);
2710        let b = result.values[0]
2711            .as_any()
2712            .downcast_ref::<FHIRBoolean>()
2713            .unwrap();
2714        assert_eq!(b.value, Some(true));
2715
2716        let result = engine
2717            .evaluate("6 < 6", vec![])
2718            .await
2719            .expect("Failed to evaluate join()");
2720
2721        assert_eq!(result.values.len(), 1);
2722        let b = result.values[0]
2723            .as_any()
2724            .downcast_ref::<FHIRBoolean>()
2725            .unwrap();
2726        assert_eq!(b.value, Some(false));
2727
2728        let result = engine
2729            .evaluate("6 < 7", vec![])
2730            .await
2731            .expect("Failed to evaluate join()");
2732
2733        assert_eq!(result.values.len(), 1);
2734        let b = result.values[0]
2735            .as_any()
2736            .downcast_ref::<FHIRBoolean>()
2737            .unwrap();
2738        assert_eq!(b.value, Some(true));
2739    }
2740}