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};
18use 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
31static 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
157fn 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 (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 "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 "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 } 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 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 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
728struct 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 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 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 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 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 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) = ¶m.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 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 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 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 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 --> (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 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}