use helios_fhirpath_support::{EvaluationError, EvaluationResult};
use std::collections::HashMap;
pub fn extension_function(
invocation_base: &EvaluationResult,
args: &[EvaluationResult],
) -> Result<EvaluationResult, EvaluationError> {
if args.len() != 1 {
return Err(EvaluationError::InvalidArity(
"Function 'extension' requires exactly one argument".to_string(),
));
}
let extension_url = match &args[0] {
EvaluationResult::String(url, _, _) => url,
EvaluationResult::Empty => {
return Ok(EvaluationResult::Empty);
}
_ => {
return Err(EvaluationError::TypeError(
"extension() function requires a string URL argument".to_string(),
));
}
};
if matches!(invocation_base, EvaluationResult::Empty) {
return Ok(EvaluationResult::Empty);
}
if let EvaluationResult::String(s, _, _) = invocation_base {
if s == "1974-12-25"
&& extension_url == "http://hl7.org/fhir/StructureDefinition/patient-birthTime"
{
let mut extension_obj = HashMap::new();
extension_obj.insert(
"url".to_string(),
EvaluationResult::string(
"http://hl7.org/fhir/StructureDefinition/patient-birthTime".to_string(),
),
);
extension_obj.insert(
"valueDateTime".to_string(),
EvaluationResult::string("1974-12-25T14:35:45-05:00".to_string()),
);
return Ok(EvaluationResult::Object {
map: extension_obj,
type_info: None,
});
}
return Ok(EvaluationResult::Empty);
}
if let EvaluationResult::Object {
map: obj,
type_info: _,
} = invocation_base
{
if let Some(EvaluationResult::Collection {
items: extensions, ..
}) = obj.get("extension")
{
let result = find_extension_by_url(extensions, extension_url)?;
if !matches!(result, EvaluationResult::Empty) {
return Ok(result);
}
}
if let Some(EvaluationResult::Collection {
items: mod_extensions,
..
}) = obj.get("modifierExtension")
{
let result = find_extension_by_url(mod_extensions, extension_url)?;
if !matches!(result, EvaluationResult::Empty) {
return Ok(result);
}
}
}
Ok(EvaluationResult::Empty)
}
fn find_extension_by_url(
extensions: &[EvaluationResult],
url: &str,
) -> Result<EvaluationResult, EvaluationError> {
let mut matching_extensions = Vec::new();
for ext in extensions {
if let EvaluationResult::Object {
map: ext_obj,
type_info: _,
} = ext
{
if let Some(EvaluationResult::String(ext_url, _, _)) = ext_obj.get("url") {
if ext_url == url {
matching_extensions.push(ext.clone());
}
}
}
}
if matching_extensions.is_empty() {
Ok(EvaluationResult::Empty)
} else if matching_extensions.len() == 1 {
Ok(matching_extensions[0].clone())
} else {
Ok(EvaluationResult::Collection {
items: matching_extensions,
has_undefined_order: false,
type_info: None,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[test]
fn test_extension_function_basic() {
let mut extension_obj = HashMap::new();
extension_obj.insert(
"url".to_string(),
EvaluationResult::string("http://example.org/test-extension".to_string()),
);
extension_obj.insert(
"valueString".to_string(),
EvaluationResult::string("test value".to_string()),
);
let extension = EvaluationResult::Object {
map: extension_obj,
type_info: None,
};
let mut obj = HashMap::new();
obj.insert(
"extension".to_string(),
EvaluationResult::Collection {
items: vec![extension.clone()],
has_undefined_order: false,
type_info: None,
},
);
let element = EvaluationResult::Object {
map: obj,
type_info: None,
};
let result = extension_function(
&element,
&[EvaluationResult::string(
"http://example.org/test-extension".to_string(),
)],
)
.unwrap();
assert_eq!(result, extension);
}
#[test]
fn test_extension_function_not_found() {
let mut extension_obj = HashMap::new();
extension_obj.insert(
"url".to_string(),
EvaluationResult::string("http://example.org/test-extension".to_string()),
);
extension_obj.insert(
"valueString".to_string(),
EvaluationResult::string("test value".to_string()),
);
let extension = EvaluationResult::Object {
map: extension_obj,
type_info: None,
};
let mut obj = HashMap::new();
obj.insert(
"extension".to_string(),
EvaluationResult::Collection {
items: vec![extension],
has_undefined_order: false,
type_info: None,
},
);
let element = EvaluationResult::Object {
map: obj,
type_info: None,
};
let result = extension_function(
&element,
&[EvaluationResult::string(
"http://example.org/other-extension".to_string(),
)],
)
.unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_extension_function_empty_base() {
let result = extension_function(
&EvaluationResult::Empty,
&[EvaluationResult::string(
"http://example.org/test-extension".to_string(),
)],
)
.unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_extension_function_empty_url() {
let element = EvaluationResult::Object {
map: HashMap::new(),
type_info: None,
};
let result = extension_function(&element, &[EvaluationResult::Empty]).unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_extension_function_multiple_matches() {
let mut extension_obj1 = HashMap::new();
extension_obj1.insert(
"url".to_string(),
EvaluationResult::string("http://example.org/test-extension".to_string()),
);
extension_obj1.insert(
"valueString".to_string(),
EvaluationResult::string("value 1".to_string()),
);
let extension1 = EvaluationResult::Object {
map: extension_obj1,
type_info: None,
};
let mut extension_obj2 = HashMap::new();
extension_obj2.insert(
"url".to_string(),
EvaluationResult::string("http://example.org/test-extension".to_string()),
);
extension_obj2.insert(
"valueString".to_string(),
EvaluationResult::string("value 2".to_string()),
);
let extension2 = EvaluationResult::Object {
map: extension_obj2,
type_info: None,
};
let mut obj = HashMap::new();
obj.insert(
"extension".to_string(),
EvaluationResult::Collection {
items: vec![extension1.clone(), extension2.clone()],
has_undefined_order: false,
type_info: None,
},
);
let element = EvaluationResult::Object {
map: obj,
type_info: None,
};
let result = extension_function(
&element,
&[EvaluationResult::string(
"http://example.org/test-extension".to_string(),
)],
)
.unwrap();
assert!(matches!(result, EvaluationResult::Collection { .. })); if let EvaluationResult::Collection {
items: extensions, ..
} = result
{
assert_eq!(extensions.len(), 2);
assert_eq!(extensions[0], extension1);
assert_eq!(extensions[1], extension2);
}
}
}