#![crate_name = "mkhtmllib"]
extern crate fs_extra;
extern crate walkdir;
use fs_extra::dir::{copy, CopyOptions};
use std::env::current_dir;
use std::fs::{create_dir, remove_dir_all, File};
use std::io::{Read, Write};
use std::path::PathBuf;
use walkdir::WalkDir;
use Error::{CopyFailed, ReadFailed, RemoveFailed, WriteFailed};
pub fn mkhtml(config: Config) -> Result<(), Error> {
if config.clone().get_build_dir().is_dir() {
let rm = remove_dir_all(config.clone().get_build_dir());
if rm.is_err() {
return Err(RemoveFailed);
}
}
for d in config.clone().iter() {
chk_dir(d)?;
}
let files = WalkDir::new(config.clone().get_pages_dir()).follow_links(true);
for file in files {
let f = file.unwrap();
if f.path().is_dir() {
let base_path = f.path().as_os_str().to_os_string().into_string().unwrap();
let from = config.clone().get_pages_dir().canonicalize().unwrap().into_os_string().into_string().unwrap();
let to = config.clone().get_pages_dir().canonicalize().unwrap().into_os_string().into_string().unwrap();
let final_path = str::replace(&base_path, &from, &to);
chk_dir(PathBuf::from(final_path))?;
} else {
let watermark_str =
"<!-- Built with mkhtml 3 (https://github.com/jusdepatate/mkhtml) -->".to_string();
let base_path = f.path().as_os_str().to_os_string().into_string().unwrap();
let from = config.clone().get_pages_dir().canonicalize().unwrap().into_os_string().into_string().unwrap();
let to = config.clone().get_build_dir().canonicalize().unwrap().into_os_string().into_string().unwrap();
let final_path = str::replace(&base_path, &from, &to);
let header = read_file(config.clone().get_parts_dir().join("header.html"))?;
let footer = read_file(config.clone().get_parts_dir().join("footer.html"))?;
let file_body = watermark_str + "\n" +
&*header + "\n" +
&*read_file(PathBuf::from(base_path))? + "\n" +
&*footer;
write_file(PathBuf::from(final_path.clone()), file_body)?;
};
};
let copy = copy(config.clone().get_static_dir(), config.clone().get_build_dir(), &CopyOptions::new());
if copy.is_err() {
return Err(CopyFailed)
}
const VERSION: &str = env!("CARGO_PKG_VERSION");
if VERSION == "dry" {
remove_dir_all(config.get_build_dir()).unwrap();
}
Ok(())
}
fn write_file(path: PathBuf, content: String) -> Result<(), Error> {
let create = File::create(&path);
if create.is_err() {
return Err(WriteFailed)
}
let mut file = create.unwrap();
let write = file.write_all(content.as_bytes());
if write.is_err() {
return Err(WriteFailed)
}
Ok(())
}
fn read_file(path: PathBuf) -> Result<String, Error> {
let open = File::open(path);
if open.is_err() {
return Err(ReadFailed)
}
let mut file = open.unwrap();
let mut content = "".to_string();
return match file.read_to_string(&mut content) {
Ok(_) => Ok(content),
Err(_) => Err(ReadFailed),
}
}
fn chk_dir(path: PathBuf) -> Result<(), Error>{
if !path.is_dir() {
let create = create_dir(path);
if create.is_err() {
return Err(WriteFailed);
}
}
Ok(())
}
#[derive(Debug)]
pub enum Error {
WriteFailed,
RemoveFailed,
CopyFailed,
ReadFailed,
}
#[derive(Clone)]
pub struct Config {
pages_dir: PathBuf,
parts_dir: PathBuf,
static_dir: PathBuf,
build_dir: PathBuf,
}
impl Config {
pub fn new() -> Config {
let cwd = current_dir().unwrap();
Config {
pages_dir: cwd.join("pages"),
parts_dir: cwd.join("parts"),
static_dir: cwd.join("static"),
build_dir: cwd.join("builds"),
}
}
pub fn iter(self) -> [PathBuf; 4] {
return [self.pages_dir, self.parts_dir, self.static_dir, self.build_dir]
}
pub fn get_pages_dir(self) -> PathBuf { return self.pages_dir }
pub fn get_parts_dir(self) -> PathBuf { return self.parts_dir }
pub fn get_static_dir(self) -> PathBuf { return self.static_dir }
pub fn get_build_dir(self) -> PathBuf { return self.build_dir }
pub fn set_pages_dir(&mut self, path: PathBuf) { self.pages_dir = path }
pub fn set_parts_dir(&mut self, path: PathBuf) { self.parts_dir = path }
pub fn set_static_dir(&mut self, path: PathBuf) { self.static_dir = path }
pub fn set_build_dir(&mut self, path: PathBuf) { self.build_dir = path }
}
#[cfg(test)]
mod tests {
use {chk_dir, read_file, write_file};
use std::env::current_dir;
use std::fs::remove_file;
use std::path::PathBuf;
use walkdir::WalkDir;
#[test]
fn test_write_file() {
write_file(current_dir().unwrap().join("a"), "test".to_string()).unwrap();
remove_file(current_dir().unwrap().join("a")).unwrap();
}
#[test]
#[should_panic]
fn test_write_file_panic() {
write_file(PathBuf::from("/"), "test".to_string()).unwrap();
}
#[test]
fn test_read_file() {
for file in WalkDir::new("..").into_iter().filter_map(|file| file.ok()) {
if file.metadata().unwrap().is_file() {
read_file(PathBuf::from(file.path())).unwrap();
return
}
}
}
#[test]
#[should_panic]
fn test_read_file_panic() {
read_file(PathBuf::from("/")).unwrap();
}
#[test]
fn test_chk_dir() {
chk_dir(current_dir().unwrap()).unwrap();
}
#[test]
#[should_panic]
fn test_chk_dir_panic() {
chk_dir(PathBuf::from("/b3VpCg==/")).unwrap();
}
}