1pub mod char_reader;
2pub mod lmarkdown;
3pub mod parse_error;
4pub mod renderer;
5pub mod sitetree;
6
7pub mod html;
8pub mod lssg_error;
9mod path_extension;
10mod tree;
11
12use std::{
13 fs::{create_dir, create_dir_all, remove_dir_all, write, File},
14 io::{self},
15 path::PathBuf,
16};
17
18use log::info;
19use lssg_error::LssgError;
20use renderer::{Renderer, RendererModule};
21use sitetree::Input;
22
23use crate::{
24 path_extension::PathExtension,
25 sitetree::{Relation, SiteNodeKind, SiteTree},
26};
27
28pub struct Lssg {
29 input: Input,
30 output_directory: PathBuf,
31 renderer: Renderer,
32}
33
34impl Lssg {
35 pub fn new(input: Input, output_directory: PathBuf) -> Lssg {
36 let renderer = Renderer::new();
37 Lssg {
38 input,
39 output_directory,
40 renderer,
41 }
42 }
43
44 pub fn add_module(&mut self, module: impl RendererModule + 'static) {
45 self.renderer.add_module(module)
46 }
47
48 pub fn render(&mut self) -> Result<(), LssgError> {
49 info!("Generating SiteTree");
50 let mut site_tree = SiteTree::from_input(self.input.clone())?;
51
52 self.renderer.init(&mut site_tree);
53 info!("SiteTree:\n{site_tree}");
54
55 self.renderer.after_init(&mut site_tree);
58
59 if self.output_directory.exists() {
60 info!(
61 "Removing {:?}",
62 self.output_directory.canonicalize_nonexistent_path()
63 );
64 remove_dir_all(&self.output_directory)?;
65 }
66 info!(
67 "Creating {:?}",
68 self.output_directory.canonicalize_nonexistent_path()
69 );
70 create_dir_all(&self.output_directory)?;
71
72 let mut queue: Vec<usize> = vec![site_tree.root()];
73 while let Some(site_id) = queue.pop() {
74 queue.append(&mut site_tree[site_id].children.clone());
75 let rel_path = site_tree.rel_path(site_tree.root(), site_id);
76 let path = self
77 .output_directory
78 .join(rel_path)
79 .canonicalize_nonexistent_path();
80 match &mut site_tree[site_id].kind {
81 SiteNodeKind::Stylesheet(stylesheet) => {
82 let mut stylesheet = stylesheet.clone();
83
84 for link in site_tree.links_from(site_id) {
86 if let Relation::Discovered { raw_path } = &link.relation {
87 let updated_resource = site_tree.path(link.to);
88 stylesheet.update_resource(raw_path, &updated_resource);
89 }
90 }
91
92 stylesheet.write(&path)?;
93 }
94 SiteNodeKind::Resource(resource) => {
95 resource.write(&path)?;
96 }
97 SiteNodeKind::Folder => {
98 info!("Creating folder {path:?}",);
99 create_dir(path)?;
100 }
101 SiteNodeKind::Page { .. } => {
102 let html = self.renderer.render(&site_tree, site_id)?;
103 create_dir_all(&path)?;
104 let html_output_path = path.join("index.html").canonicalize_nonexistent_path();
105
106 info!(
107 "Writing to {:?}",
108 (&html_output_path).canonicalize_nonexistent_path()
109 );
110 write(html_output_path, html)?;
111 }
112 }
113 }
114
115 info!("All files written");
116
117 Ok(())
118 }
119}