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>(
16 x_fhir_query: &str,
17 values: Vec<&'a dyn MetaValue>,
18 config: Arc<Config<'b>>,
19) -> Result<String, OperationOutcomeError>
20where
21 'a: 'b,
22{
23 let engine = FPEngine::new();
24
25 let mut result = x_fhir_query.to_string();
26
27 for expression in FP_EXPRESSION_REGEX.captures_iter(x_fhir_query) {
28 let full_match = expression.get(0).map(|m| m.as_str()).unwrap_or("");
29
30 let expr = expression.get(1).map(|m| m.as_str()).unwrap_or("");
31
32 println!("Evaluating FHIRPath expression: '{}'", expr);
33
34 if expr.is_empty() {
35 return Err(OperationOutcomeError::fatal(
36 IssueType::Invalid(None),
37 "FHIRPath expression is empty.".to_string(),
38 ));
39 }
40
41 let fp_result = engine
42 .evaluate_with_config(expr, values.clone(), config.clone())
43 .await
44 .map_err(|e| {
45 OperationOutcomeError::fatal(
46 IssueType::Invalid(None),
47 format!("FHIRPath evaluation error: {}", e),
48 )
49 })?;
50
51 let fp_string_result = fp_result
52 .iter()
53 .map(|v| stringify_meta_value(v))
54 .collect::<Result<Vec<String>, OperationOutcomeError>>()?
55 .join(",");
56
57 result = result.replace(full_match, &fp_string_result);
58 }
59
60 Ok(result)
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use haste_fhir_model::r4::generated::{
67 resources::Patient,
68 types::{FHIRString, HumanName},
69 };
70 #[tokio::test]
71 async fn test_simple_eval() {
72 let patient = Patient {
73 id: Some("example".to_string()),
74
75 ..Default::default()
76 };
77 let result = evaluation(
78 "Patient/{{$this.id}}",
79 vec![&patient],
80 Arc::new(Config {
81 variable_resolver: None,
82 }),
83 )
84 .await
85 .expect("Evaluation failed");
86
87 assert_eq!(result, "Patient/example");
88 }
89
90 #[tokio::test]
91 async fn test_multiple() {
92 let patient = Patient {
93 id: Some("example".to_string()),
94 name: Some(vec![Box::new(HumanName {
95 family: Some(Box::new(FHIRString {
96 value: Some("Doe".to_string()),
97 ..Default::default()
98 })),
99 ..Default::default()
100 })]),
101 ..Default::default()
102 };
103 let result = evaluation(
104 "Patient/{{$this.id}}/{{$this.name.family.value}}",
105 vec![&patient],
106 Arc::new(Config {
107 variable_resolver: None,
108 }),
109 )
110 .await
111 .expect("Evaluation failed");
112
113 assert_eq!(result, "Patient/example/Doe");
114 }
115}