lssg_lib/renderer/
renderer.rs

1use std::cell::{RefCell, UnsafeCell};
2use std::rc::Rc;
3
4use log::{debug, error, info, warn};
5
6use crate::sitetree::Input;
7use crate::{
8    html::DomTree,
9    sitetree::{SiteNodeKind, SiteTree},
10    LssgError,
11};
12
13use super::{modules::RendererModule, DefaultModule};
14use super::{RenderContext, TokenRenderer};
15
16/// HtmlRenderer is responsible for the process of converting the site tree into the final HTML output.
17/// It does this by managing a queue of tokens to be rendered and delegating the rendering process to different modules.
18pub struct Renderer {
19    modules: Vec<Box<dyn RendererModule>>,
20}
21
22impl Renderer {
23    pub fn new() -> Renderer {
24        Renderer { modules: vec![] }
25    }
26
27    pub fn add_module(&mut self, module: impl RendererModule + 'static) {
28        self.modules.push(Box::new(module));
29    }
30
31    /// Will run init on all modules, will remove modules if it fails
32    pub fn init(&mut self, site_tree: &mut SiteTree) {
33        debug!("running init");
34        let failed: Vec<usize> = self
35            .modules
36            .iter_mut()
37            .enumerate()
38            .filter_map(|(i, module)| match module.init(site_tree) {
39                Ok(_) => None,
40                Err(e) => {
41                    error!("Failed to do site_init on {}: {e}", module.id());
42                    Some(i)
43                }
44            })
45            .collect();
46        for i in failed.into_iter().rev() {
47            self.modules.remove(i);
48        }
49    }
50
51    /// Will run after_init on all modules, will remove modules if it fails
52    pub fn after_init(&mut self, site_tree: &SiteTree) {
53        debug!("running after_init");
54        let failed: Vec<usize> = self
55            .modules
56            .iter_mut()
57            .enumerate()
58            .filter_map(|(i, module)| match module.after_init(site_tree) {
59                Ok(_) => None,
60                Err(e) => {
61                    error!("Failed to do site_init on {}: {e}", module.id());
62                    Some(i)
63                }
64            })
65            .collect();
66        for i in failed.into_iter().rev() {
67            self.modules.remove(i);
68        }
69    }
70
71    /// Transform site id into a html page
72    pub fn render(&mut self, site_tree: &SiteTree, site_id: usize) -> Result<String, LssgError> {
73        // get the site node
74        let site_node = site_tree.get(site_id)?;
75        let page = match &site_node.kind {
76            SiteNodeKind::Page(page) => page,
77            _ => return Err(LssgError::render("Invalid node type given")),
78        };
79
80        let mut dom = DomTree::new();
81
82        let context = RenderContext {
83            input: site_tree.get_input(site_id),
84            site_tree,
85            site_id,
86            page,
87        };
88
89        // initialize modules
90        for module in &mut self.modules {
91            debug!("running render_page on {}", module.id());
92            module.render_page(&mut dom, &context);
93        }
94
95        debug!("running render_body on modules");
96        let tr = TokenRenderer::new(&mut self.modules);
97        tr.start_render(&mut dom, &context);
98
99        for module in &mut self.modules {
100            debug!("running after_render on {}", module.id());
101            module.after_render(&mut dom, &context);
102        }
103
104        // sanitize html
105        dom.validate();
106
107        // println!("{dom}");
108        // println!("{dom:?}");
109        // println!("{:?}", tree.get_mut(9));
110        // println!("{page:#?}");
111        return Ok(dom.to_html_string());
112    }
113}