use crate::core::eval;
use crate::core::exec::ExecOutcome;
use crate::core::Expr;
use crate::error::{Result, TemplateError};
use crate::template::TModel;
pub struct VisitNode {
pub expr: Option<Expr>,
#[allow(dead_code)]
pub using: Option<Expr>,
}
impl VisitNode {
pub fn new(expr: Option<Expr>, using: Option<Expr>) -> Self {
VisitNode { expr, using }
}
pub(crate) fn exec(&self, env: &mut crate::core::Environment) -> Result<ExecOutcome> {
let node = match &self.expr {
Some(e) => eval::eval(env, e)?,
None => env.get_current_visitor_node().ok_or_else(|| {
TemplateError::misc(
"#visit must be given a node, or be called while visiting a node",
)
})?,
};
env.push_visitor_node(node.clone());
let r = visit_node(env, &node);
env.pop_visitor_node();
r
}
}
pub(crate) fn visit_node(env: &mut crate::core::Environment, node: &TModel) -> Result<ExecOutcome> {
let node_name = match &node.node {
Some(n) => n.name()?.unwrap_or_default(),
None => String::new(),
};
let ns = env.get_current_namespace();
let macro_name = if !node_name.is_empty() {
match &node.node {
Some(n) => match n.namespace()? {
Some(uri) if !uri.is_empty() => {
match env.current_ns_prefixes().get_prefix_for_namespace(&uri) {
Some(p) if !p.is_empty() => format!("{p}:{node_name}"),
Some(_) => node_name.clone(),
None => String::new(),
}
}
_ => node_name.clone(),
},
None => String::new(),
}
} else {
String::new()
};
if !macro_name.is_empty() {
if let Some(m) = ns.get_member(¯o_name) {
if let Some(mv) = env.as_macro(&m) {
return crate::core::unified_call::call_macro(env, &mv, &[], None, Vec::new());
}
}
}
if let Some(m) = ns.get_member("@default") {
if let Some(mv) = env.as_macro(&m) {
return crate::core::unified_call::call_macro(env, &mv, &[], None, Vec::new());
}
}
let node_type = match &node.node {
Some(n) => n.node_type()?,
None => String::new(),
};
match node_type.as_str() {
"text" | "comment" | "pi" | "attribute" => {
if let Some(s) = &node.scalar {
let text = s.as_string()?;
env.emit(&text)?;
}
Ok(ExecOutcome::Done)
}
_ => {
let children = match &node.node {
Some(n) => n.children()?,
None => Vec::new(),
};
for c in children {
env.push_visitor_node(c.clone());
let r = visit_node(env, &c);
env.pop_visitor_node();
r?;
}
Ok(ExecOutcome::Done)
}
}
}