use serde_json::Value;
pub trait ArticleVisitor {
fn visit_article_start(&mut self, id: u64, name: &str);
fn visit_category(&mut self, name: &str, url: &str);
fn visit_link(&mut self, text: &str, url: &str);
fn visit_infobox(&mut self, name: &str, value: &str);
fn visit_reference(&mut self, id: &str, ref_type: &str, metadata: &Value);
fn visit_article_end(&mut self);
}
pub struct NoOpVisitor;
impl ArticleVisitor for NoOpVisitor {
fn visit_article_start(&mut self, _id: u64, _name: &str) {}
fn visit_category(&mut self, _name: &str, _url: &str) {}
fn visit_link(&mut self, _text: &str, _url: &str) {}
fn visit_infobox(&mut self, _name: &str, _value: &str) {}
fn visit_reference(&mut self, _id: &str, _ref_type: &str, _metadata: &Value) {}
fn visit_article_end(&mut self) {}
}
pub struct StatsVisitor {
pub article_count: u64,
pub category_count: u64,
pub link_count: u64,
pub infobox_count: u64,
pub reference_count: u64,
}
impl StatsVisitor {
pub fn new() -> Self {
Self {
article_count: 0,
category_count: 0,
link_count: 0,
infobox_count: 0,
reference_count: 0,
}
}
}
impl ArticleVisitor for StatsVisitor {
fn visit_article_start(&mut self, _id: u64, _name: &str) {
self.article_count += 1;
}
fn visit_category(&mut self, _name: &str, _url: &str) {
self.category_count += 1;
}
fn visit_link(&mut self, _text: &str, _url: &str) {
self.link_count += 1;
}
fn visit_infobox(&mut self, _name: &str, _value: &str) {
self.infobox_count += 1;
}
fn visit_reference(&mut self, _id: &str, _ref_type: &str, _metadata: &Value) {
self.reference_count += 1;
}
fn visit_article_end(&mut self) {}
}
impl Default for StatsVisitor {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_noop_visitor() {
let mut visitor = NoOpVisitor;
visitor.visit_article_start(1, "Test");
visitor.visit_category("Cat", "https://example.com");
visitor.visit_link("Link", "https://example.com");
visitor.visit_infobox("Name", "Value");
visitor.visit_reference("ref1", "web", &Value::Null);
visitor.visit_article_end();
}
#[test]
fn test_stats_visitor_new() {
let visitor = StatsVisitor::new();
assert_eq!(visitor.article_count, 0);
assert_eq!(visitor.category_count, 0);
assert_eq!(visitor.link_count, 0);
assert_eq!(visitor.infobox_count, 0);
assert_eq!(visitor.reference_count, 0);
}
#[test]
fn test_stats_visitor_counts() {
let mut visitor = StatsVisitor::new();
for i in 0..10 {
visitor.visit_article_start(i, &format!("Article {}", i));
visitor.visit_category("Category:A", "https://example.com/A");
visitor.visit_category("Category:B", "https://example.com/B");
visitor.visit_link("Link1", "https://example.com/1");
visitor.visit_link("Link2", "https://example.com/2");
visitor.visit_link("Link3", "https://example.com/3");
visitor.visit_infobox("Name", "Value");
visitor.visit_reference(&format!("ref{}", i * 2), "web", &Value::Null);
visitor.visit_reference(&format!("ref{}", i * 2 + 1), "book", &Value::Null);
visitor.visit_article_end();
}
assert_eq!(visitor.article_count, 10);
assert_eq!(visitor.category_count, 20); assert_eq!(visitor.link_count, 30); assert_eq!(visitor.infobox_count, 10); assert_eq!(visitor.reference_count, 20); }
#[test]
fn test_stats_visitor_default() {
let visitor: StatsVisitor = Default::default();
assert_eq!(visitor.article_count, 0);
assert_eq!(visitor.category_count, 0);
}
struct TestVisitor {
last_article_id: Option<u64>,
last_article_name: Option<String>,
categories: Vec<String>,
}
impl ArticleVisitor for TestVisitor {
fn visit_article_start(&mut self, id: u64, name: &str) {
self.last_article_id = Some(id);
self.last_article_name = Some(name.to_string());
}
fn visit_category(&mut self, name: &str, _url: &str) {
self.categories.push(name.to_string());
}
fn visit_link(&mut self, _text: &str, _url: &str) {}
fn visit_infobox(&mut self, _name: &str, _value: &str) {}
fn visit_reference(&mut self, _id: &str, _ref_type: &str, _metadata: &Value) {}
fn visit_article_end(&mut self) {}
}
#[test]
fn test_custom_visitor() {
let mut visitor = TestVisitor {
last_article_id: None,
last_article_name: None,
categories: Vec::new(),
};
visitor.visit_article_start(42, "Test Article");
visitor.visit_category("Category:Test", "https://example.com");
visitor.visit_article_end();
assert_eq!(visitor.last_article_id, Some(42));
assert_eq!(visitor.last_article_name, Some("Test Article".to_string()));
assert_eq!(visitor.categories, vec!["Category:Test"]);
}
}