1pub mod char_reader;
19pub mod lmarkdown;
20pub mod parse_error;
21pub mod renderer;
22pub mod sitetree;
23
24mod domnode_to_token;
25pub mod lssg_error;
26mod path_extension;
27mod tree;
28
29use std::{
30 fs::{create_dir, create_dir_all, remove_dir_all, write},
31 path::PathBuf,
32};
33
34use log::info;
35use lssg_error::LssgError;
36use renderer::Renderer;
37use sitetree::Input;
38
39use crate::{
40 path_extension::PathExtension,
41 sitetree::{Relation, SiteId, SiteNodeKind, SiteTree},
42};
43
44pub struct Lssg {
45 input: Input,
46 output_directory: PathBuf,
47 renderer: Renderer,
48}
49
50impl Lssg {
51 pub fn new(input: Input, output_directory: PathBuf, renderer: Renderer) -> Lssg {
52 Lssg {
53 input,
54 output_directory,
55 renderer,
56 }
57 }
58
59 pub fn render(&mut self) -> Result<(), LssgError> {
60 info!("Generating SiteTree");
61 let mut site_tree = SiteTree::from_input(self.input.clone())?;
62
63 self.renderer.init(&mut site_tree);
64 info!("SiteTree:\n{site_tree}");
65
66 self.renderer.after_init(&site_tree);
69
70 if self.output_directory.exists() {
71 info!(
72 "Removing {:?}",
73 self.output_directory.canonicalize_nonexistent_path()
74 );
75 remove_dir_all(&self.output_directory)?;
76 }
77 info!(
78 "Creating {:?}",
79 self.output_directory.canonicalize_nonexistent_path()
80 );
81 create_dir_all(&self.output_directory)?;
82
83 let mut queue: Vec<SiteId> = vec![site_tree.root()];
84 while let Some(site_id) = queue.pop() {
85 queue.append(&mut site_tree[site_id].children.clone());
86 let rel_path = site_tree.rel_path(site_tree.root(), site_id);
87 let path = self
88 .output_directory
89 .join(rel_path)
90 .canonicalize_nonexistent_path();
91 match &site_tree[site_id].kind {
92 SiteNodeKind::Javascript(javascript) => {
93 let mut javascript = javascript.clone();
94
95 for link in site_tree.links_from(site_id) {
97 if let Relation::Discovered { raw_path } = &link.relation {
98 let updated_resource = site_tree.rel_path(
99 site_tree[site_id]
100 .parent
101 .expect("stylesheet must have parent"),
102 link.to,
103 );
104 javascript.update_resource(raw_path, &updated_resource);
105 }
106 }
107
108 javascript.write(&path)?;
109 }
110 SiteNodeKind::Stylesheet(stylesheet) => {
111 let mut stylesheet = stylesheet.clone();
112
113 for link in site_tree.links_from(site_id) {
115 if let Relation::Discovered { raw_path } = &link.relation {
116 let updated_resource = site_tree.rel_path(
117 site_tree[site_id]
118 .parent
119 .expect("stylesheet must have parent"),
120 link.to,
121 );
122 stylesheet.update_resource(raw_path, &updated_resource);
123 }
124 }
125
126 stylesheet.write(&path)?;
127 }
128 SiteNodeKind::Resource(resource) => {
129 if let Err(e) = resource.write(&path) {
130 log::error!("Failed to write resource to {path:?}: {e}")
131 }
132 }
133 SiteNodeKind::Folder => {
134 info!("Creating folder {path:?}",);
135 create_dir(path)?;
136 }
137 SiteNodeKind::Page { .. } => {
138 let html = self.renderer.render(&site_tree, site_id)?;
139 create_dir_all(&path)?;
140 let html_output_path = path.join("index.html").canonicalize_nonexistent_path();
141
142 info!(
143 "Writing to {:?}",
144 html_output_path.canonicalize_nonexistent_path()
145 );
146 write(html_output_path, html)?;
147 }
148 }
149 }
150
151 info!("All files written");
152
153 Ok(())
154 }
155}