use std::sync::Arc;
use crate::Result;
use crate::plugins::Plugin;
use crate::types::ExtractedDocument;
use crate::types::internal::InternalDocument;
pub trait Renderer: Plugin {
fn render_result(&self, result: &ExtractedDocument) -> Result<String> {
Ok(result.content.clone())
}
}
pub(crate) trait InternalRenderer: Plugin {
fn render(&self, doc: &InternalDocument) -> Result<String>;
}
impl<T> Renderer for T
where
T: InternalRenderer + ?Sized,
{
fn render_result(&self, result: &ExtractedDocument) -> Result<String> {
match result.internal_document.as_ref() {
Some(doc) => InternalRenderer::render(self, doc),
None => Ok(result.content.clone()),
}
}
}
pub fn register_renderer(renderer: Arc<dyn Renderer>) -> Result<()> {
use crate::plugins::registry::get_renderer_registry;
let registry = get_renderer_registry();
let mut registry = registry.write();
registry.register(renderer)
}
pub fn unregister_renderer(name: &str) -> Result<()> {
use crate::plugins::registry::get_renderer_registry;
let registry = get_renderer_registry();
let mut registry = registry.write();
registry.remove(name)
}
pub fn list_renderers() -> Result<Vec<String>> {
use crate::plugins::registry::get_renderer_registry;
let registry = get_renderer_registry();
let registry = registry.read();
Ok(registry.list())
}
pub fn clear_renderers() -> Result<()> {
use crate::plugins::registry::get_renderer_registry;
let registry = get_renderer_registry();
let mut registry = registry.write();
registry.clear_all()
}
pub(crate) fn ensure_renderers_initialized() {
use crate::plugins::registry::get_renderer_registry;
let registry = get_renderer_registry();
{
let registry = registry.read();
if !registry.is_missing_builtin_renderer() {
return;
}
}
registry.write().ensure_defaults();
}
#[cfg(test)]
mod tests {
use super::*;
use crate::plugins::registry::test_support::RendererRegistryGuard;
struct MockRenderer {
format: &'static str,
}
impl Plugin for MockRenderer {
fn name(&self) -> &str {
self.format
}
}
impl InternalRenderer for MockRenderer {
fn render(&self, doc: &crate::types::internal::InternalDocument) -> crate::Result<String> {
Ok(format!("mock-{}-{}", self.format, doc.elements.len()))
}
}
#[test]
fn register_list_unregister_roundtrip() {
let _guard = RendererRegistryGuard::acquire();
register_renderer(Arc::new(MockRenderer { format: "test-fmt-a" })).unwrap();
assert!(list_renderers().unwrap().contains(&"test-fmt-a".to_string()));
unregister_renderer("test-fmt-a").unwrap();
assert!(!list_renderers().unwrap().contains(&"test-fmt-a".to_string()));
}
#[test]
fn render_result_renders_from_the_preserved_internal_document() {
use crate::types::internal::{ElementKind, InternalElement};
let renderer = MockRenderer { format: "test-fmt-c" };
let mut doc = InternalDocument::new("text/plain");
doc.push_element(InternalElement::text(ElementKind::Paragraph, "one", 0));
doc.push_element(InternalElement::text(ElementKind::Paragraph, "two", 0));
let mut result = ExtractedDocument {
content: "one\n\ntwo".to_string(),
..Default::default()
};
result.internal_document = Some(doc);
assert_eq!(
Renderer::render_result(&renderer, &result).unwrap(),
"mock-test-fmt-c-2"
);
}
#[test]
fn render_result_falls_back_to_content_without_an_internal_document() {
let renderer = MockRenderer { format: "test-fmt-d" };
let result = ExtractedDocument {
content: "already rendered".to_string(),
..Default::default()
};
assert_eq!(Renderer::render_result(&renderer, &result).unwrap(), "already rendered");
}
#[test]
fn register_list_clear_list_roundtrip() {
let _guard = RendererRegistryGuard::acquire();
register_renderer(Arc::new(MockRenderer { format: "test-fmt-b" })).unwrap();
assert!(list_renderers().unwrap().contains(&"test-fmt-b".to_string()));
clear_renderers().unwrap();
assert!(list_renderers().unwrap().is_empty());
}
}