1#![crate_name = "mkhtmllib"]
2extern crate fs_extra;
19extern crate walkdir;
20
21use fs_extra::dir::{copy, CopyOptions};
22use std::env::current_dir;
23use std::fs::{create_dir, remove_dir_all, File};
24use std::io::{Read, Write};
25use std::path::PathBuf;
26use walkdir::WalkDir;
27use Error::{CopyFailed, ReadFailed, RemoveFailed, WriteFailed};
28
29
30pub fn mkhtml(config: Config) -> Result<(), Error> {
43 if config.clone().get_build_dir().is_dir() {
46 let rm = remove_dir_all(config.clone().get_build_dir());
47
48 if rm.is_err() {
49 return Err(RemoveFailed);
50 }
51 }
52
53 for d in config.clone().iter() {
55 chk_dir(d)?;
57 }
58
59 let files = WalkDir::new(config.clone().get_pages_dir()).follow_links(true);
61
62 for file in files {
63 let f = file.unwrap();
64
65 if f.path().is_dir() {
67 let base_path = f.path().as_os_str().to_os_string().into_string().unwrap();
69
70 let from = config.clone().get_pages_dir().canonicalize().unwrap().into_os_string().into_string().unwrap();
71 let to = config.clone().get_pages_dir().canonicalize().unwrap().into_os_string().into_string().unwrap();
72
73 let final_path = str::replace(&base_path, &from, &to);
74
75 chk_dir(PathBuf::from(final_path))?;
77 } else {
78 let watermark_str =
80 "<!-- Built with mkhtml 3 (https://github.com/jusdepatate/mkhtml) -->".to_string();
81
82 let base_path = f.path().as_os_str().to_os_string().into_string().unwrap();
84 let from = config.clone().get_pages_dir().canonicalize().unwrap().into_os_string().into_string().unwrap();
85 let to = config.clone().get_build_dir().canonicalize().unwrap().into_os_string().into_string().unwrap();
86 let final_path = str::replace(&base_path, &from, &to);
87
88 let header = read_file(config.clone().get_parts_dir().join("header.html"))?;
90 let footer = read_file(config.clone().get_parts_dir().join("footer.html"))?;
91
92 let file_body = watermark_str + "\n" +
94 &*header + "\n" +
95 &*read_file(PathBuf::from(base_path))? + "\n" +
96 &*footer;
97
98 write_file(PathBuf::from(final_path.clone()), file_body)?;
100 };
101 };
102
103 let copy = copy(config.clone().get_static_dir(), config.clone().get_build_dir(), &CopyOptions::new());
105 if copy.is_err() {
106 return Err(CopyFailed)
107 }
108
109 const VERSION: &str = env!("CARGO_PKG_VERSION");
110 if VERSION == "dry" {
111 remove_dir_all(config.get_build_dir()).unwrap();
112 }
113
114 Ok(())
115}
116
117fn write_file(path: PathBuf, content: String) -> Result<(), Error> {
126 let create = File::create(&path);
128 if create.is_err() {
129 return Err(WriteFailed)
130 }
131 let mut file = create.unwrap();
132
133 let write = file.write_all(content.as_bytes());
135
136 if write.is_err() {
137 return Err(WriteFailed)
138 }
139 Ok(())
140}
141
142fn read_file(path: PathBuf) -> Result<String, Error> {
151 let open = File::open(path);
153 if open.is_err() {
154 return Err(ReadFailed)
155 }
156
157 let mut file = open.unwrap();
158
159 let mut content = "".to_string();
161 return match file.read_to_string(&mut content) {
162 Ok(_) => Ok(content),
163 Err(_) => Err(ReadFailed),
164 }
165}
166
167fn chk_dir(path: PathBuf) -> Result<(), Error>{
174 if !path.is_dir() {
176 let create = create_dir(path);
178 if create.is_err() {
179 return Err(WriteFailed);
180 }
181 }
182 Ok(())
183}
184
185#[derive(Debug)]
187pub enum Error {
188 WriteFailed,
189 RemoveFailed,
190 CopyFailed,
191 ReadFailed,
192}
193
194#[derive(Clone)]
201pub struct Config {
202 pages_dir: PathBuf,
203 parts_dir: PathBuf,
204 static_dir: PathBuf,
205 build_dir: PathBuf,
206}
207
208impl Config {
209 pub fn new() -> Config {
211 let cwd = current_dir().unwrap();
213
214 Config {
216 pages_dir: cwd.join("pages"),
217 parts_dir: cwd.join("parts"),
218 static_dir: cwd.join("static"),
219 build_dir: cwd.join("builds"),
220 }
221 }
222
223 pub fn iter(self) -> [PathBuf; 4] {
225 return [self.pages_dir, self.parts_dir, self.static_dir, self.build_dir]
226 }
227
228 pub fn get_pages_dir(self) -> PathBuf { return self.pages_dir }
229 pub fn get_parts_dir(self) -> PathBuf { return self.parts_dir }
230 pub fn get_static_dir(self) -> PathBuf { return self.static_dir }
231 pub fn get_build_dir(self) -> PathBuf { return self.build_dir }
232
233 pub fn set_pages_dir(&mut self, path: PathBuf) { self.pages_dir = path }
234 pub fn set_parts_dir(&mut self, path: PathBuf) { self.parts_dir = path }
235 pub fn set_static_dir(&mut self, path: PathBuf) { self.static_dir = path }
236 pub fn set_build_dir(&mut self, path: PathBuf) { self.build_dir = path }
237}
238
239#[cfg(test)]
240mod tests {
241 use {chk_dir, read_file, write_file};
242 use std::env::current_dir;
243 use std::fs::remove_file;
244 use std::path::PathBuf;
245 use walkdir::WalkDir;
246
247 #[test]
248 fn test_write_file() {
249 write_file(current_dir().unwrap().join("a"), "test".to_string()).unwrap();
251 remove_file(current_dir().unwrap().join("a")).unwrap();
252 }
253
254 #[test]
255 #[should_panic]
256 fn test_write_file_panic() {
257 write_file(PathBuf::from("/"), "test".to_string()).unwrap();
259 }
260
261 #[test]
262 fn test_read_file() {
263 for file in WalkDir::new("..").into_iter().filter_map(|file| file.ok()) {
265 if file.metadata().unwrap().is_file() {
266 read_file(PathBuf::from(file.path())).unwrap();
267 return
268 }
269 }
270 }
271
272 #[test]
273 #[should_panic]
274 fn test_read_file_panic() {
275 read_file(PathBuf::from("/")).unwrap();
277 }
278
279 #[test]
280 fn test_chk_dir() {
281 chk_dir(current_dir().unwrap()).unwrap();
283 }
284
285 #[test]
286 #[should_panic]
287 fn test_chk_dir_panic() {
288 chk_dir(PathBuf::from("/b3VpCg==/")).unwrap();
290 }
291}