haste_x_fhir_query/
lib.rs1use haste_fhir_model::r4::generated::terminology::IssueType;
2use haste_fhir_operation_error::OperationOutcomeError;
3use haste_fhirpath::{Config, FPEngine};
4use haste_reflect::MetaValue;
5use regex::Regex;
6use std::sync::{Arc, LazyLock};
7
8use crate::conversion::stringify_meta_value;
9
10pub mod conversion;
11
12static FP_EXPRESSION_REGEX: LazyLock<Regex> =
13 LazyLock::new(|| Regex::new(r"\{\{([^}]*)\}\}").expect("Failed to compile regex"));
14
15pub async fn evaluation<'a, 'b>(
27 x_fhir_query: &str,
28 values: Vec<&'a dyn MetaValue>,
29 config: Arc<Config<'b>>,
30) -> Result<String, OperationOutcomeError>
31where
32 'a: 'b,
33{
34 let engine = FPEngine::new();
35
36 let mut result = x_fhir_query.to_string();
37
38 for expression in FP_EXPRESSION_REGEX.captures_iter(x_fhir_query) {
39 let full_match = expression.get(0).map_or("", |m| m.as_str());
40
41 let expr = expression.get(1).map_or("", |m| m.as_str());
42
43 println!("Evaluating FHIRPath expression: '{expr}'");
44
45 if expr.is_empty() {
46 return Err(OperationOutcomeError::fatal(
47 IssueType::invalid(),
48 "FHIRPath expression is empty.".to_string(),
49 ));
50 }
51
52 let fp_result = engine
53 .evaluate_with_config(expr, values.clone(), config.clone())
54 .await
55 .map_err(|e| {
56 OperationOutcomeError::fatal(
57 IssueType::invalid(),
58 format!("FHIRPath evaluation error: {e}"),
59 )
60 })?;
61
62 let fp_string_result = fp_result
63 .iter()
64 .map(stringify_meta_value)
65 .collect::<Result<Vec<String>, OperationOutcomeError>>()?
66 .join(",");
67
68 result = result.replace(full_match, &fp_string_result);
69 }
70
71 Ok(result)
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use haste_fhir_model::r4::generated::{
78 resources::Patient,
79 types::{FHIRString, HumanName},
80 };
81 #[tokio::test]
82 async fn test_simple_eval() {
83 let patient = Patient {
84 id: Some("example".to_string()),
85
86 ..Default::default()
87 };
88 let result = evaluation(
89 "Patient/{{$this.id}}",
90 vec![&patient],
91 Arc::new(Config::default()),
92 )
93 .await
94 .expect("Evaluation failed");
95
96 assert_eq!(result, "Patient/example");
97 }
98
99 #[tokio::test]
100 async fn test_multiple() {
101 let patient = Patient {
102 id: Some("example".to_string()),
103 name: Some(vec![HumanName {
104 family: Some(Box::new(FHIRString {
105 value: Some("Doe".to_string()),
106 ..Default::default()
107 })),
108 ..Default::default()
109 }]),
110 ..Default::default()
111 };
112 let result = evaluation(
113 "Patient/{{$this.id}}/{{$this.name.family.value}}",
114 vec![&patient],
115 Arc::new(Config::default()),
116 )
117 .await
118 .expect("Evaluation failed");
119
120 assert_eq!(result, "Patient/example/Doe");
121 }
122}