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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/// Builds the website into the output directory
mod base_template;

use relative_path::RelativePath;
use std::fs;
use tera::{Context, Tera};
use toml::Value;

use crate::{
    config_handler::Config,
    content_processor::{process_md, process_toml, split_front_matter},
    debug_print::dprintln,
};

pub(crate) fn build(root_dir: &str, drafts: bool, output: &str) {
    // Clear output folder
    if fs::metadata(output).is_ok() {
        fs::remove_dir_all(output).expect("Couldn't clear output directory");
    }
    fs::create_dir(output).expect("Couldn't create output directory");

    // Compile templates
    let mut template_path =
        RelativePath::new("./templates/").to_logical_path(root_dir).to_string_lossy().to_string();
    template_path.push_str("/**/*");
    let mut tera = match Tera::parse(template_path.replace("\\", "/").as_str()) {
        Ok(t) => t,
        Err(e) => {
            eprintln!("Parsing error: {}", e);
            return;
        }
    };

    // Add built-in base template
    if let Err(e) = tera.add_raw_template("base.html", base_template::BASE) {
        eprintln!("Error adding the base template: {}", e);
    }
    tera.build_inheritance_chains().expect("Couldn't build inheritance chain");

    // loop through all files in content & process them individually
    let files = match RelativePath::new("./content").to_logical_path(root_dir).read_dir() {
        Ok(files) => files,
        Err(e) => panic!("Encountered an error while parsing content folder: {}", e),
    };

    for file in files {
        let filename = &file.as_ref().unwrap().file_name().to_string_lossy().to_string();
        let filename = filename.as_str();
        dprintln!("Processing file: {}", filename);

        let content = if let Ok(content) = fs::read_to_string(&file.as_ref().unwrap().path()) {
            content.as_str().replace('\r', "")
        } else {
            eprintln!("Failed reading {}", filename);
            return;
        };

        let loaded_toml;
        let processed_markdown: Option<String>;
        if filename.ends_with(".md") {
            // Separate markdown and toml
            let (toml, markdown) = split_front_matter(content.as_str()).unwrap();
            loaded_toml = process_toml(toml);
            processed_markdown = Some(process_md(markdown));
        } else if filename.ends_with(".toml") {
            // Process toml
            loaded_toml = process_toml(&content);
            processed_markdown = None;
        } else {
            continue; // skip non toml/md files
        }
        // Decide whether to compile drafts
        match loaded_toml.get_key_value("draft") {
            Some((_, Value::Boolean(true))) => {
                if drafts {
                    () // compile drafts when --drafts set
                } else {
                    continue;
                } // skip cycle
            }
            _ => (), // always compile non drafts
        }

        // Pass parsed md/toml to the templating engine
        let mut context = Context::new();
        context.insert("Config", Config::global());
        context.insert("Page", &loaded_toml);
        context.insert("Content", &processed_markdown);

        let to_render = if loaded_toml.contains_key("template") {
            match loaded_toml.get_key_value("template") {
                Some((_, Value::String(v))) => v,
                _ => {
                    dprintln!("Couldn't find 'template' variable");
                    "base.html"
                }
            }
        } else {
            "base.html"
        };

        // Render to file
        match tera.render(to_render, &context) {
            Ok(to_write) => {
                let temp = file.unwrap().path();
                let relative_filepath =
                    temp.strip_prefix(root_dir).unwrap().strip_prefix("content").unwrap();
                let mut filepath =
                    RelativePath::new(&relative_filepath.to_string_lossy()).to_logical_path(output);
                filepath.set_extension("html");

                match fs::write(filepath, to_write) {
                    Ok(_) => {
                        dprintln!("File write succesful");
                    }
                    Err(e) => {
                        eprintln!(
                            "Encountered error: {} while writing processed file to output",
                            e
                        );
                        return;
                    }
                }
            }
            Err(e) => {
                eprintln!("Error while rendering file: {} into a template: {}", filename, e);
                return;
            }
        }
    }

    // loop through all files in assets
    let files = match RelativePath::new("./assets").to_logical_path(root_dir).read_dir() {
        Ok(files) => files,
        Err(_e) => panic!("Encountered an error while parsing assets folder"),
    };
    for file in files {
        let filename = (&file).as_ref().unwrap().file_name().to_string_lossy().to_string();
        let filename = filename.as_str();

        match fs::copy(
            file.unwrap().path(),
            RelativePath::new(&format!("./{}", filename)).to_logical_path(output),
        ) {
            Ok(_) => (),
            Err(e) => eprintln!("Error copying asset file {}: {}", &filename, e),
        }
    }
}