use std::cell::RefCell;
use std::rc::Rc;
use crate::error::{ConversionError, Result};
use crate::visitor::HtmlVisitor;
use crate::visitor::VisitResult;
use super::content::VisitorDispatch;
#[allow(dead_code)]
#[inline]
pub fn dispatch_visitor<F>(visitor: &Option<Rc<RefCell<dyn HtmlVisitor>>>, callback: F) -> Result<VisitorDispatch>
where
F: FnOnce(&mut dyn HtmlVisitor) -> VisitResult,
{
let Some(visitor_rc) = visitor else {
return Ok(VisitorDispatch::Continue);
};
let mut visitor_ref = visitor_rc.borrow_mut();
let result = callback(&mut *visitor_ref);
match result {
VisitResult::Continue => Ok(VisitorDispatch::Continue),
VisitResult::Custom(output) => Ok(VisitorDispatch::Custom(output)),
VisitResult::Skip => Ok(VisitorDispatch::Skip),
VisitResult::PreserveHtml => Ok(VisitorDispatch::PreserveHtml),
VisitResult::Error(msg) => Err(ConversionError::Visitor(msg)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
use crate::visitor::{NodeContext, NodeType};
#[derive(Debug)]
struct TestVisitor {
mode: TestMode,
}
#[derive(Debug)]
enum TestMode {
Continue,
Custom,
Skip,
PreserveHtml,
Error,
}
impl HtmlVisitor for TestVisitor {
fn visit_text(&mut self, _ctx: &NodeContext, text: &str) -> VisitResult {
match self.mode {
TestMode::Continue => VisitResult::Continue,
TestMode::Custom => VisitResult::Custom(format!("CUSTOM: {}", text)),
TestMode::Skip => VisitResult::Skip,
TestMode::PreserveHtml => VisitResult::PreserveHtml,
TestMode::Error => VisitResult::Error("test error".to_string()),
}
}
}
#[test]
fn test_dispatch_visitor_none() {
let visitor: Option<Rc<RefCell<dyn HtmlVisitor>>> = None;
let result = dispatch_visitor(&visitor, |v| {
let ctx = NodeContext {
node_type: NodeType::Text,
tag_name: String::new(),
attributes: BTreeMap::new(),
depth: 0,
index_in_parent: 0,
parent_tag: None,
is_inline: true,
};
v.visit_text(&ctx, "test")
})
.unwrap();
assert!(result.is_continue());
}
#[test]
fn test_dispatch_visitor_continue() {
let visitor: Rc<RefCell<dyn HtmlVisitor>> = Rc::new(RefCell::new(TestVisitor {
mode: TestMode::Continue,
}));
let visitor_opt = Some(visitor);
let ctx = NodeContext {
node_type: NodeType::Text,
tag_name: String::new(),
attributes: BTreeMap::new(),
depth: 0,
index_in_parent: 0,
parent_tag: None,
is_inline: true,
};
let result = dispatch_visitor(&visitor_opt, |v| v.visit_text(&ctx, "hello")).unwrap();
assert!(result.is_continue());
}
#[test]
fn test_dispatch_visitor_custom() {
let visitor: Rc<RefCell<dyn HtmlVisitor>> = Rc::new(RefCell::new(TestVisitor { mode: TestMode::Custom }));
let visitor_opt = Some(visitor);
let ctx = NodeContext {
node_type: NodeType::Text,
tag_name: String::new(),
attributes: BTreeMap::new(),
depth: 0,
index_in_parent: 0,
parent_tag: None,
is_inline: true,
};
let result = dispatch_visitor(&visitor_opt, |v| v.visit_text(&ctx, "hello")).unwrap();
assert!(result.is_custom());
assert_eq!(result.as_custom(), Some("CUSTOM: hello"));
}
#[test]
fn test_dispatch_visitor_skip() {
let visitor: Rc<RefCell<dyn HtmlVisitor>> = Rc::new(RefCell::new(TestVisitor { mode: TestMode::Skip }));
let visitor_opt = Some(visitor);
let ctx = NodeContext {
node_type: NodeType::Text,
tag_name: String::new(),
attributes: BTreeMap::new(),
depth: 0,
index_in_parent: 0,
parent_tag: None,
is_inline: true,
};
let result = dispatch_visitor(&visitor_opt, |v| v.visit_text(&ctx, "hello")).unwrap();
assert!(result.is_skip());
}
#[test]
fn test_dispatch_visitor_preserve_html() {
let visitor: Rc<RefCell<dyn HtmlVisitor>> = Rc::new(RefCell::new(TestVisitor {
mode: TestMode::PreserveHtml,
}));
let visitor_opt = Some(visitor);
let ctx = NodeContext {
node_type: NodeType::Text,
tag_name: String::new(),
attributes: BTreeMap::new(),
depth: 0,
index_in_parent: 0,
parent_tag: None,
is_inline: true,
};
let result = dispatch_visitor(&visitor_opt, |v| v.visit_text(&ctx, "hello")).unwrap();
assert!(result.is_preserve_html());
}
#[test]
fn test_dispatch_visitor_error() {
let visitor: Rc<RefCell<dyn HtmlVisitor>> = Rc::new(RefCell::new(TestVisitor { mode: TestMode::Error }));
let visitor_opt = Some(visitor);
let ctx = NodeContext {
node_type: NodeType::Text,
tag_name: String::new(),
attributes: BTreeMap::new(),
depth: 0,
index_in_parent: 0,
parent_tag: None,
is_inline: true,
};
let result = dispatch_visitor(&visitor_opt, |v| v.visit_text(&ctx, "hello"));
assert!(result.is_err());
if let Err(ConversionError::Visitor(msg)) = result {
assert_eq!(msg, "test error");
} else {
panic!("Expected Visitor error");
}
}
}