1use core::fmt;
2use std::{
3 collections::HashMap,
4 ops::{Index, IndexMut},
5};
6
7use log::{debug, warn};
8
9use crate::{tree::Tree, LssgError};
10
11use super::{
12 page::Page,
13 relational_graph::RelationalGraph,
14 relational_graph::{Link, Relation},
15 stylesheet::Stylesheet,
16 Input, Resource, SiteNode, SiteNodeKind,
17};
18
19fn absolute_path(nodes: &Vec<SiteNode>, to: SiteId) -> String {
20 let mut names = vec![nodes[to].name.clone()];
21 let mut parent = nodes[to].parent;
22 while let Some(p) = parent {
23 names.push(nodes[p].name.clone());
24 parent = nodes[p].parent;
25 }
26 names.pop(); names.reverse();
28 return format!("/{}", names.join("/"));
29}
30
31fn rel_path(nodes: &Vec<SiteNode>, from: SiteId, to: SiteId) -> String {
33 let mut visited = HashMap::new();
34 let mut to_path = vec![nodes[to].name.clone()];
35
36 let mut depth = 0;
38 let mut node = nodes[to].parent;
39 while let Some(i) = node {
40 visited.insert(i, depth);
41 depth += 1;
42 node = nodes[i].parent;
43 if let Some(_) = nodes[i].parent {
45 to_path.push(nodes[i].name.clone())
46 }
47 }
48
49 depth = 0;
51 let mut to_depth = to_path.len() - 1;
52 let mut node = Some(from);
53 while let Some(i) = node {
54 if let Some(d) = visited.get(&i) {
55 to_depth = *d;
56 break;
57 }
58 depth += 1;
59 node = nodes[i].parent;
60 }
61
62 to_path.reverse();
64 let to_path = if nodes[to].parent.is_some() {
65 to_path[to_path.len() - 1 - to_depth..to_path.len()].join("/")
66 } else {
67 depth -= 1;
68 "".into()
69 };
70
71 if depth > 0 {
73 return format!("{}{}", "../".repeat(depth), to_path);
74 } else {
75 return format!("./{}", to_path);
76 }
77}
78
79pub type SiteId = usize;
80
81#[derive(Debug)]
83pub struct SiteTree {
84 nodes: Vec<SiteNode>,
85 root: SiteId,
86 root_input: Input,
88
89 input_to_id: HashMap<Input, SiteId>,
91 rel_graph: RelationalGraph,
92}
93
94impl SiteTree {
95 pub fn from_input(input: Input) -> Result<SiteTree, LssgError> {
97 let mut tree = SiteTree {
98 nodes: vec![],
99 root: 0,
100 root_input: input.clone(),
101 input_to_id: HashMap::new(),
102 rel_graph: RelationalGraph::new(),
103 };
104 tree.add_page_with_root(input, None)?;
105 Ok(tree)
106 }
107
108 pub fn is_parent(&self, id: SiteId, parent_id: SiteId) -> bool {
110 let mut parent = self.nodes[id].parent;
111 while let Some(p) = parent {
112 if p == parent_id {
113 return true;
114 }
115 parent = self.nodes[id].parent
116 }
117 return false;
118 }
119
120 pub fn get_input(&self, id: SiteId) -> Option<&Input> {
122 self.input_to_id
123 .iter()
124 .find_map(|(input, i)| if *i == id { Some(input) } else { None })
125 }
126
127 pub fn get_by_name(&self, name: &str, id: SiteId) -> Option<&SiteId> {
129 self.nodes[id]
130 .children
131 .iter()
132 .find(|n| &self.nodes[**n].name == name)
133 }
134
135 pub fn root(&self) -> SiteId {
136 self.root
137 }
138
139 pub fn get(&self, id: SiteId) -> Result<&SiteNode, LssgError> {
140 self.nodes.get(id).ok_or(LssgError::sitetree(&format!(
141 "Could not find {id} in SiteTree"
142 )))
143 }
144
145 pub fn page_parent(&self, id: SiteId) -> Option<SiteId> {
147 let mut parent = self.nodes[id].parent;
148 let mut parents = vec![];
149 while let Some(p) = parent {
150 if let SiteNodeKind::Page { .. } = self.nodes[p].kind {
151 return Some(p);
152 }
153 parents.push(p);
154 parent = self.nodes[p].parent;
155 }
156 None
157 }
158
159 pub fn parents(&self, id: SiteId) -> Vec<SiteId> {
161 let mut parent = self.nodes[id].parent;
162 let mut parents = vec![];
163 while let Some(p) = parent {
164 parents.push(p);
165 parent = self.nodes[p].parent;
166 }
167 parents
168 }
169
170 pub fn path(&self, id: SiteId) -> String {
172 absolute_path(&self.nodes, id)
173 }
174
175 pub fn rel_path(&self, from: SiteId, to: SiteId) -> String {
177 rel_path(&self.nodes, from, to)
178 }
179
180 pub fn ids(&self) -> Vec<SiteId> {
181 (0..self.nodes.len() - 1).collect()
182 }
183
184 pub fn add_link(&mut self, from: SiteId, to: SiteId) {
185 self.rel_graph.add(from, to, Relation::External);
186 }
187
188 pub fn links_from(&self, from: SiteId) -> Vec<&Link> {
190 self.rel_graph.links_from(from)
191 }
192
193 pub fn add(&mut self, node: SiteNode) -> Result<SiteId, LssgError> {
195 if let Some(parent) = node.parent {
197 if let Some(id) = self.get_by_name(&node.name, parent) {
198 warn!("{} already exists at {id}", node.name);
199 return Ok(*id);
200 }
201 }
202
203 let id = self.nodes.len();
204 if let Some(parent) = node.parent {
205 self.nodes[parent].children.push(id);
206 self.rel_graph.add(parent, id, Relation::Family);
207 }
208 self.nodes.push(node);
209
210 Ok(id)
211 }
212
213 pub fn add_from_input(
216 &mut self,
217 input: Input,
218 mut parent_id: SiteId,
219 ) -> Result<SiteId, LssgError> {
220 if let Some(id) = self.input_to_id.get(&input) {
222 warn!(
223 "{} already exists using existing node instead",
224 input.filename()?
225 );
226 return Ok(*id);
227 }
228
229 let id = if SiteNodeKind::input_is_stylesheet(&input) {
230 self.add_stylesheet_from_input(input.clone(), parent_id)?
231 } else if SiteNodeKind::input_is_page(&input) {
232 self.add_page_from_input(input.clone(), parent_id)?
233 } else {
234 parent_id = self.create_folders(&input, parent_id)?;
235 let id = self.add(SiteNode {
236 name: input.filename()?,
237 parent: Some(parent_id),
238 children: vec![],
239 kind: SiteNodeKind::Resource(Resource::from_input(&input)?),
240 })?;
241 self.input_to_id.insert(input.clone(), id);
242 id
243 };
244
245 Ok(id)
246 }
247
248 fn add_page_from_input(&mut self, input: Input, parent: SiteId) -> Result<SiteId, LssgError> {
251 self.add_page_with_root(input, Some(parent))
252 }
253
254 fn add_page_with_root(
256 &mut self,
257 input: Input,
258 mut parent: Option<SiteId>,
259 ) -> Result<SiteId, LssgError> {
260 if let Some(id) = self.input_to_id.get(&input) {
261 return Ok(*id);
263 }
264
265 if let Some(parent) = &mut parent {
266 *parent = self.create_folders(&input, *parent)?;
267 }
268
269 let page = Page::from_input(&input)?;
271 let id = self.add(SiteNode {
272 name: input.filestem().unwrap_or("root".to_string()),
273 parent,
274 children: vec![],
275 kind: SiteNodeKind::Page(page),
276 })?;
277
278 self.input_to_id.insert(input.clone(), id);
280
281 let links: Vec<(bool, String)> = match &self.nodes[id].kind {
282 SiteNodeKind::Page(page) => page
283 .links()
284 .into_iter()
285 .map(|(text, href)| (text.len() == 0, href.clone()))
286 .collect(),
287 _ => panic!("has to be page"),
288 };
289
290 for (is_empty, href) in links {
291 if is_empty {
293 let input = input.new(&href)?;
294 let child_id = self.add_from_input(input, id)?;
295 self.rel_graph
296 .add(id, child_id, Relation::Discovered { raw_path: href });
297 continue;
298 }
299
300 if Page::is_href_to_page(&href) {
301 let input = input.new(&href)?;
302 let child_id = self.add_page_from_input(input, id)?;
303 self.rel_graph
304 .add(id, child_id, Relation::Discovered { raw_path: href });
305 continue;
306 }
307 }
308
309 return Ok(id);
310 }
311
312 pub fn add_stylesheet_from_input(
314 &mut self,
315 input: Input,
316 mut parent: SiteId,
317 ) -> Result<SiteId, LssgError> {
318 parent = self.create_folders(&input, parent)?;
319
320 let stylesheet = Stylesheet::try_from(&input)?;
321 let resources: Vec<String> = stylesheet
322 .resources()
323 .into_iter()
324 .map(|p| p.to_string())
325 .collect();
326
327 let parent = self.create_folders(&input, parent)?;
328 let stylesheet_id = self.add(SiteNode {
329 name: input.filename()?,
330 parent: Some(parent),
331 children: vec![],
332 kind: SiteNodeKind::Stylesheet(stylesheet),
333 })?;
334
335 for resource in resources {
336 let input = input.new(&resource)?;
337 let parent = self.create_folders(&input, parent)?;
338 let resource_id = self.add(SiteNode {
339 name: input.filename()?,
340 parent: Some(parent),
341 children: vec![],
342 kind: SiteNodeKind::Resource(Resource::from_input(&input)?),
343 })?;
344 self.rel_graph.add(
345 stylesheet_id,
346 resource_id,
347 Relation::Discovered { raw_path: resource },
348 );
349 self.input_to_id.insert(input, resource_id);
350 }
351
352 self.input_to_id.insert(input, stylesheet_id);
354
355 Ok(stylesheet_id)
356 }
357
358 fn create_folders(&mut self, input: &Input, mut parent: SiteId) -> Result<SiteId, LssgError> {
361 if let Some(rel_path) = self.root_input.make_relative(input) {
362 if rel_path.starts_with("..") {
364 return Ok(parent);
365 }
366 let parts: Vec<&str> = rel_path.split("/").collect();
367 let parts = &parts[0..parts.len() - 1];
368
369 let mut parents = self.parents(parent);
370 parents.push(parent);
371 parents.reverse();
372 for i in 0..parts.len() {
373 let name = parts[i];
374 if let Some(parent) = parents.get(i) {
375 if self[*parent].name == name {
376 continue;
377 }
378 }
379 if let Some(id) = self.get_by_name(name, parent) {
380 parent = *id;
381 } else {
382 debug!("creating folder {name:?} under {parent:?}");
383 parent = self.add(SiteNode {
384 name: name.to_string(),
385 parent: Some(parent),
386 children: vec![],
387 kind: SiteNodeKind::Folder,
388 })?;
389 }
390 }
391 }
392 return Ok(parent);
393 }
394
395 pub fn remove(&mut self, id: SiteId) {
396 self.rel_graph.remove_all(id);
397 todo!("remove from tree");
398 }
399
400 pub fn minify(&mut self) {
402 todo!()
404 }
405}
406
407impl Tree for SiteTree {
408 type Node = SiteNode;
409
410 fn root(&self) -> SiteId {
411 self.root
412 }
413
414 fn get(&self, id: SiteId) -> &Self::Node {
415 &self[id]
416 }
417}
418
419impl fmt::Display for SiteTree {
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 let mut row_length = 0;
423 let mut table: Vec<Vec<Option<String>>> = vec![];
424 let mut prev_col = 0;
425 let mut queue = vec![(self.root(), 0)];
426 while let Some((n, col)) = queue.pop() {
427 let node = &self.nodes[n];
428 for c in &node.children {
429 queue.push((c.clone(), col + 1))
430 }
431
432 if let None = table.get(col) {
434 table.push(vec![]);
435 }
436
437 let amount_rows_in_col = table[col].len();
439 if prev_col > col {
441 for _ in amount_rows_in_col..row_length {
442 table[col].push(None);
443 }
444 } else {
445 for _ in amount_rows_in_col + 1..row_length {
447 table[col].push(None);
448 }
449 }
450 prev_col = col;
451
452 let node_name = format!("{}({})({})", node.name, n, node.kind.to_string());
453 table[col].push(Some(node_name));
454
455 let amount_rows_in_col = table[col].len();
456 if amount_rows_in_col > row_length {
458 row_length = amount_rows_in_col;
459 }
460 }
461
462 let mut out = vec![String::new(); row_length];
464 for col in 0..table.len() {
465 let max_name_length = table[col]
466 .iter()
467 .map(|c| c.as_ref().map(|c| c.len()).unwrap_or(0))
468 .reduce(|a, b| a.max(b))
469 .unwrap_or(0);
470 for (row, entry) in table[col].iter().enumerate() {
471 match entry {
472 Some(name) => {
473 out[row] += name;
474 out[row] += &" ".repeat(max_name_length - name.len());
475 if let Some(next_column) = table.get(col + 1) {
476 if let Some(Some(_)) = next_column.get(row) {
477 out[row] += &" - ";
478 continue;
479 }
480 }
481 out[row] += &" ";
482 }
483 None => out[row] += &" ".repeat(max_name_length + 3),
484 }
485 }
486 for row in table[col].len()..row_length {
487 out[row] += &" ".repeat(max_name_length + 3);
488 }
489 }
490
491 f.write_str(&out.join("\n"))?;
492 Ok(())
493 }
494}
495
496impl Index<SiteId> for SiteTree {
497 type Output = SiteNode;
498
499 fn index(&self, index: SiteId) -> &Self::Output {
500 &self.nodes[index]
501 }
502}
503impl IndexMut<SiteId> for SiteTree {
504 fn index_mut(&mut self, index: SiteId) -> &mut Self::Output {
505 &mut self.nodes[index]
506 }
507}