use crate::plugins::Renderer;
use crate::types::internal::InternalDocument;
use crate::{KreuzbergError, Result};
use ahash::AHashMap;
use std::sync::Arc;
struct MarkdownRenderer;
impl Renderer for MarkdownRenderer {
fn name(&self) -> &str {
"markdown"
}
fn render(&self, doc: &InternalDocument) -> Result<String> {
Ok(crate::rendering::render_markdown(doc))
}
}
struct HtmlRenderer;
impl Renderer for HtmlRenderer {
fn name(&self) -> &str {
"html"
}
fn render(&self, doc: &InternalDocument) -> Result<String> {
Ok(crate::rendering::render_html(doc))
}
}
struct DjotRenderer;
impl Renderer for DjotRenderer {
fn name(&self) -> &str {
"djot"
}
fn render(&self, doc: &InternalDocument) -> Result<String> {
Ok(crate::rendering::render_djot(doc))
}
}
struct PlainRenderer;
impl Renderer for PlainRenderer {
fn name(&self) -> &str {
"plain"
}
fn render(&self, doc: &InternalDocument) -> Result<String> {
Ok(crate::rendering::render_plain(doc))
}
}
pub struct RendererRegistry {
renderers: AHashMap<String, Arc<dyn Renderer>>,
}
impl RendererRegistry {
pub fn new() -> Self {
let mut registry = Self {
renderers: AHashMap::new(),
};
registry.register_builtins();
registry
}
pub fn new_empty() -> Self {
Self {
renderers: AHashMap::new(),
}
}
fn register_builtins(&mut self) {
self.renderers
.insert("markdown".to_string(), Arc::new(MarkdownRenderer));
self.renderers.insert("html".to_string(), Arc::new(HtmlRenderer));
self.renderers.insert("djot".to_string(), Arc::new(DjotRenderer));
self.renderers.insert("plain".to_string(), Arc::new(PlainRenderer));
}
pub fn register(&mut self, renderer: Arc<dyn Renderer>) -> Result<()> {
let name = renderer.name().to_string();
super::validate_plugin_name(&name)?;
self.renderers.insert(name, renderer);
Ok(())
}
pub fn get(&self, name: &str) -> Result<Arc<dyn Renderer>> {
self.renderers.get(name).cloned().ok_or_else(|| KreuzbergError::Plugin {
message: format!("Renderer '{}' not registered", name),
plugin_name: name.to_string(),
})
}
pub fn render(&self, name: &str, doc: &InternalDocument) -> Result<String> {
let renderer = self.get(name)?;
renderer.render(doc)
}
pub fn list(&self) -> Vec<String> {
self.renderers.keys().cloned().collect()
}
pub fn remove(&mut self, name: &str) {
self.renderers.remove(name);
}
pub fn reset_to_defaults(&mut self) -> Result<()> {
self.renderers.clear();
self.register_builtins();
Ok(())
}
}
impl Default for RendererRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockRenderer {
format_name: String,
}
impl Renderer for MockRenderer {
fn name(&self) -> &str {
&self.format_name
}
fn render(&self, doc: &InternalDocument) -> Result<String> {
Ok(format!("mock-rendered-{}-elements", doc.elements.len()))
}
}
#[test]
fn test_renderer_registry_new_has_builtins() {
let registry = RendererRegistry::new();
let names = registry.list();
assert!(names.contains(&"markdown".to_string()));
assert!(names.contains(&"html".to_string()));
assert!(names.contains(&"djot".to_string()));
assert!(names.contains(&"plain".to_string()));
}
#[test]
fn test_renderer_registry_new_empty() {
let registry = RendererRegistry::new_empty();
assert_eq!(registry.list().len(), 0);
}
#[test]
fn test_renderer_registry_register_and_get() {
let mut registry = RendererRegistry::new_empty();
let renderer = Arc::new(MockRenderer {
format_name: "test-format".to_string(),
});
registry.register(renderer).unwrap();
let retrieved = registry.get("test-format").unwrap();
assert_eq!(retrieved.name(), "test-format");
}
#[test]
fn test_renderer_registry_get_missing() {
let registry = RendererRegistry::new_empty();
let result = registry.get("nonexistent");
assert!(result.is_err());
}
#[test]
fn test_renderer_registry_render_convenience() {
let registry = RendererRegistry::new();
let doc = InternalDocument::new("text/plain");
let result = registry.render("plain", &doc);
assert!(result.is_ok());
}
#[test]
fn test_renderer_registry_render_missing() {
let registry = RendererRegistry::new_empty();
let doc = InternalDocument::new("text/plain");
let result = registry.render("nonexistent", &doc);
assert!(result.is_err());
}
#[test]
fn test_renderer_registry_remove() {
let mut registry = RendererRegistry::new_empty();
let renderer = Arc::new(MockRenderer {
format_name: "to-remove".to_string(),
});
registry.register(renderer).unwrap();
registry.remove("to-remove");
assert_eq!(registry.list().len(), 0);
}
#[test]
fn test_renderer_registry_reset_to_defaults() {
let mut registry = RendererRegistry::new();
let custom = Arc::new(MockRenderer {
format_name: "custom".to_string(),
});
registry.register(custom).unwrap();
assert!(registry.list().contains(&"custom".to_string()));
registry.reset_to_defaults().unwrap();
assert!(!registry.list().contains(&"custom".to_string()));
assert!(registry.list().contains(&"markdown".to_string()));
}
#[test]
fn test_renderer_registry_invalid_name_empty() {
let mut registry = RendererRegistry::new_empty();
let renderer = Arc::new(MockRenderer {
format_name: "".to_string(),
});
let result = registry.register(renderer);
assert!(matches!(result, Err(KreuzbergError::Validation { .. })));
}
#[test]
fn test_renderer_registry_invalid_name_with_spaces() {
let mut registry = RendererRegistry::new_empty();
let renderer = Arc::new(MockRenderer {
format_name: "invalid format".to_string(),
});
let result = registry.register(renderer);
assert!(matches!(result, Err(KreuzbergError::Validation { .. })));
}
#[test]
fn test_renderer_registry_builtin_markdown_renders() {
let registry = RendererRegistry::new();
let doc = InternalDocument::new("text/plain");
let result = registry.render("markdown", &doc).unwrap();
let _ = result;
}
#[test]
fn test_renderer_registry_builtin_html_renders() {
let registry = RendererRegistry::new();
let doc = InternalDocument::new("text/plain");
let result = registry.render("html", &doc).unwrap();
let _ = result;
}
#[test]
fn test_renderer_registry_builtin_djot_renders() {
let registry = RendererRegistry::new();
let doc = InternalDocument::new("text/plain");
let result = registry.render("djot", &doc).unwrap();
let _ = result;
}
#[test]
fn test_renderer_registry_builtin_plain_renders() {
let registry = RendererRegistry::new();
let doc = InternalDocument::new("text/plain");
let result = registry.render("plain", &doc).unwrap();
let _ = result;
}
}