lssg_lib/sitetree/
site_node.rs1use std::{
2 fs::{self, File},
3 io::{Cursor, Read},
4 path::{Path, PathBuf},
5};
6
7use crate::{path_extension::PathExtension, tree::Node, LssgError};
8use pathdiff::diff_paths;
9use reqwest::Url;
10
11use super::stylesheet::Stylesheet;
12use super::{page::Page, Resource};
13
14#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub enum Input {
17 Local { path: PathBuf },
18 External { url: Url },
19}
20impl Input {
21 pub fn from_string(string: &str) -> Result<Input, LssgError> {
23 if string.starts_with("http") {
25 let url = Url::parse(&string).unwrap(); return Ok(Input::External { url });
27 }
28
29 let mut path = PathBuf::from(&string);
30 path = fs::canonicalize(path)?;
31
32 Ok(Input::Local { path })
33 }
34
35 pub fn make_relative(&self, to: &Input) -> Option<String> {
36 match self {
37 Input::Local { path: from_path } => match to {
38 Input::Local { path: to_path } => {
39 let from_path = if from_path.is_file() {
40 from_path.parent().unwrap_or(&from_path)
41 } else {
42 from_path
43 };
44 return diff_paths(to_path, from_path)
45 .map(|p| p.to_str().map(|s| s.to_string()))
46 .flatten();
47 }
48 _ => return None,
49 },
50 Input::External { url: from_url } => match to {
51 Input::External { url: to_url } => from_url.make_relative(to_url),
52 _ => return None,
53 },
54 }
55 }
56
57 pub fn is_relative(path: &str) -> bool {
59 if path.starts_with("/") || path.starts_with("http") {
60 return false;
61 }
62 return true;
63 }
64 pub fn new(&self, path_string: &str) -> Result<Input, LssgError> {
66 if path_string.starts_with("http") {
68 let url = Url::parse(&path_string).unwrap();
69 return Ok(Input::External { url });
70 }
71
72 match self {
73 Input::Local { path } => {
74 let path: &Path = if path.filename_from_path()?.contains(".") {
76 &path.parent().unwrap_or(&path)
77 } else {
78 &path
79 };
80 let mut path = path.join(path_string);
81 path = fs::canonicalize(path)?;
82 return Ok(Input::Local { path });
83 }
84 Input::External { url } => {
85 let url = url.join(path_string).unwrap(); return Ok(Input::External { url });
88 }
89 }
90 }
91 pub fn filestem(&self) -> Result<String, LssgError> {
92 match self {
93 Input::Local { path } => path.filestem_from_path(),
94 Input::External { url } => Path::new(url.path()).filestem_from_path(),
95 }
96 }
97 pub fn filename(&self) -> Result<String, LssgError> {
98 match self {
99 Input::Local { path } => path.filename_from_path(),
100 Input::External { url } => Path::new(url.path()).filename_from_path(),
101 }
102 }
103 pub fn to_string(&self) -> String {
104 match self {
105 Input::Local { path } => path.to_string_lossy().to_string(),
106 Input::External { url } => url.to_string(),
107 }
108 }
109 pub fn readable(&self) -> Result<Box<dyn Read>, LssgError> {
110 match self {
111 Input::Local { path } => {
112 let file = File::open(path)?;
113 Ok(Box::new(file))
114 }
115 Input::External { url } => {
116 let response = reqwest::blocking::get(url.clone()).unwrap();
118 let content = Cursor::new(response.bytes().unwrap());
119 Ok(Box::new(content))
120 }
121 }
122 }
123}
124
125#[derive(Debug)]
126pub enum SiteNodeKind {
127 Stylesheet(Stylesheet),
128 Page(Page),
129 Resource(Resource),
130 Folder,
131}
132impl SiteNodeKind {
133 pub fn input_is_page(input: &Input) -> bool {
134 input.to_string().ends_with(".md")
135 }
136 pub fn input_is_stylesheet(input: &Input) -> bool {
137 input.to_string().ends_with(".css")
138 }
139 pub fn is_page(&self) -> bool {
140 if let SiteNodeKind::Page { .. } = self {
141 true
142 } else {
143 false
144 }
145 }
146}
147impl ToString for SiteNodeKind {
148 fn to_string(&self) -> String {
149 match self {
150 SiteNodeKind::Stylesheet { .. } => "Stylesheet",
151 SiteNodeKind::Page { .. } => "Page",
152 SiteNodeKind::Resource { .. } => "Resource",
153 SiteNodeKind::Folder => "Folder",
154 }
155 .into()
156 }
157}
158
159#[derive(Debug)]
160pub struct SiteNode {
161 pub name: String,
163 pub parent: Option<usize>,
164 pub children: Vec<usize>,
165 pub kind: SiteNodeKind,
166}
167impl Node for SiteNode {
168 fn children(&self) -> &Vec<usize> {
169 &self.children
170 }
171}
172impl SiteNode {
173 pub fn stylesheet(name: impl Into<String>, parent: usize, stylesheet: Stylesheet) -> SiteNode {
174 SiteNode {
175 name: name.into(),
176 parent: Some(parent),
177 children: vec![],
178 kind: SiteNodeKind::Stylesheet(stylesheet),
179 }
180 }
181}