use super::Call;
use crate::eval::convert::describe;
use crate::eval::error::EvalError;
use crate::temporal::{Date, DateTime, Time};
use crate::value::{Collection, Item, TypeInfo};
pub(super) fn call<'a>(call: &Call<'_, '_, 'a>) -> Result<Option<Collection<'a>>, EvalError> {
let model = call.model();
match call.name {
"children" => {
call.arity(0, 0, "no")?;
children(call, &call.input).map(Some)
}
"descendants" => {
call.arity(0, 0, "no")?;
let mut out = Vec::new();
let mut level = children(call, &call.input)?;
while !level.is_empty() {
let next = children(call, &level)?;
out.extend(level);
level = next;
}
Ok(Some(out))
}
"trace" => {
call.arity(1, 2, "1 or 2")?;
let name = crate::eval::convert::string(model, &call.arg_or_empty(0)?, "trace")?
.unwrap_or_default();
let traced = if call.args.len() == 2 {
let mut projected = Vec::new();
for (index, item) in call.input.iter().enumerate() {
projected.extend(call.lambda(1, item, index, None)?);
}
projected
} else {
call.input.clone()
};
tracing::trace!(
target: "ferrobridge_fhirpath::trace",
name = %name,
count = traced.len(),
types = %traced.iter().map(describe).collect::<Vec<_>>().join(", "),
"trace()"
);
Ok(Some(call.input.clone()))
}
"now" | "today" | "timeOfDay" => {
call.arity(0, 0, "no")?;
let civil = call
.engine
.now()
.to_zoned(jiff::tz::TimeZone::UTC)
.datetime();
let date = format!(
"{:04}-{:02}-{:02}",
civil.year(),
civil.month(),
civil.day()
);
let time = format!(
"{:02}:{:02}:{:02}.{:03}",
civil.hour(),
civil.minute(),
civil.second(),
civil.millisecond()
);
let item = match call.name {
"now" => DateTime::parse(&format!("{date}T{time}Z")).map(Item::DateTime),
"today" => Date::parse(&date).map(Item::Date),
_ => Time::parse(&time).map(Item::Time),
};
Ok(Some(item.into_iter().collect()))
}
"type" => {
call.arity(0, 0, "no")?;
Ok(Some(
call.input
.iter()
.map(|item| {
Item::Type(match item {
Item::Node(node) => TypeInfo {
namespace: "FHIR",
name: node
.fhir_type()
.filter(|ty| !ty.is_backbone())
.map(|ty| String::from(ty.name)),
},
Item::Type(_) => TypeInfo {
namespace: "System",
name: Some(String::from("TypeInfo")),
},
other => TypeInfo {
namespace: "System",
name: other.system_type().map(String::from),
},
})
})
.collect(),
))
}
_ => Ok(None),
}
}
fn children<'a>(
call: &Call<'_, '_, 'a>,
items: &Collection<'a>,
) -> Result<Collection<'a>, EvalError> {
let mut out = Vec::new();
for item in items {
if let Item::Node(node) = item {
out.extend(
node.named_children(call.model())?
.into_iter()
.map(|(_, child)| child),
);
}
}
Ok(out)
}