haste_fhirpath/
lib.rs

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