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(())
}