Skip to main content

haste_fhirpath/
lib.rs

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