1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
pub mod util; mod article; use article::Article; use std::fs::{self, File}; use std::io::prelude::*; use std::io::BufWriter; use std::path::Path; const TMP_ROOT: &str = "tmp"; const CSS_FILE: &str = "styles.css"; static CSS_STYLES: &'static str = include_str!("../static/styles.css"); pub struct Config<'a> { pub articles_dir: &'a str, pub static_dir: Option<&'a str>, pub destination_dir: &'a str, pub email: Option<&'a str>, pub css_path: Option<&'a str>, } pub fn exec(config: Config) -> std::io::Result<()> { create_tmp()?; let default_css_path = Path::new(CSS_FILE); let mut default_css_file = File::create(default_css_path)?; default_css_file.write_all(CSS_STYLES.as_bytes())?; let css_path = match config.css_path { Some(path) => Path::new(path), None => default_css_path, }; let static_path: Option<&Path> = match config.static_dir { Some(dir) => Some(Path::new(dir)), None => None, }; let mut articles = get_articles(config.articles_dir)?; articles.reverse(); write_articles(&articles, config.email, css_path.to_str().unwrap())?; copy_files(css_path, static_path)?; write_site(config.destination_dir).unwrap_or_else(|_| panic!("Could not write {}", config.destination_dir)); fs::remove_file(default_css_path)?; Ok(()) } fn write_site(dest_dir: &str) -> std::io::Result<()> { let _ = fs::remove_dir_all(dest_dir); fs::rename(TMP_ROOT, dest_dir)?; Ok(()) } fn create_tmp() -> std::io::Result<()> { let _ = fs::remove_dir_all(TMP_ROOT); fs::create_dir(TMP_ROOT).unwrap_or_else(|_| panic!("Cannot create tmp dir at {}", TMP_ROOT)); let articles_folder = format!("{}/articles", TMP_ROOT); fs::create_dir(&articles_folder) .unwrap_or_else(|_| panic!("Cannot create dir {}", &articles_folder)); Ok(()) } pub fn get_articles(articles_dir: &str) -> std::result::Result<Vec<Article>, std::io::Error> { let mut articles: Vec<Article> = vec![]; for entry in fs::read_dir(&articles_dir).unwrap_or_else(|_| panic!("Cannot read dir {}", articles_dir)) { let entry = &entry?; let path = entry.path(); let slug = path.file_stem().unwrap().to_str().unwrap().to_owned(); let mut file = File::open(&path).unwrap(); let mut markdown = String::new(); file.read_to_string(&mut markdown) .expect("Cannot read article file"); articles.push(Article::new(markdown, slug)); } Ok(articles) } fn write_article(file_name: &str, html: &str) -> std::io::Result<()> { let file = File::create(file_name) .unwrap_or_else(|_| panic!("Cannot create article file {}", file_name)); let mut writer = BufWriter::new(&file); writer .write_all(html.as_bytes()) .unwrap_or_else(|_| panic!("Cannot write article file at {}", file_name)); Ok(()) } fn write_articles(articles: &[Article], email: Option<&str>, css: &str) -> std::io::Result<()> { let first = 0; let last = articles.len() - 1; for i in first..=last { let article = &articles[i]; let footer = Article::render_footer(i, &articles, email); let html = &article.render(&css, &footer); write_article( &format!("{}/articles/{}.html", TMP_ROOT, article.slug), &html, )?; if i == last { write_article(&format!("{}/index.html", TMP_ROOT), &html)?; } } Ok(()) } fn copy_files(css_path: &Path, static_dir: Option<&Path>) -> std::io::Result<()> { if let Some(static_dir) = static_dir { util::copy_dir(static_dir, Path::new(TMP_ROOT)).expect("Could not copy static directory"); } if let Some(css_name) = css_path.file_name() { fs::copy(css_path, Path::new(TMP_ROOT).join(css_name)).expect("Could not copy css"); } Ok(()) }