#![warn(missing_docs)]

//! # nyf
//!
//! _glyph's static site generator_
//!
//! This is an opinionated, custom-built HTML splicer. It reads HTML templates, splices
//! them into a base template and writes the output.
//!
//! Templates are expected at `./templates` and output is written to `./site`. The `./site`
//! directory will be created if it doesn't exist.
//!
//! A base template is expected at `./templates/base.html`. It must contain two tags:
//! `[[ title ]]` and `[[ content ]]`.
//!
//! The root index template is expected at `./templates/index.html`. All other templates are
//! expected to be in sub-directories; for example: `./templates/plants/index.html` or
//! `./templates/fungi/entomopathogens.html`.
//!
//! The splicer crawls the sub-directories of `./templates`, reads each HTML file, splices
//! it into the `[[ content ]]` tag of the base HTML template and writes the output to the
//! `./site` directory (preserving the sub-directory structure). The contents of the `<h2>`
//! element are spliced into the `[[ title ]]` tag of the base HTML template for each
//! sub-directory template.

use std::{fs, io, path::Path};

// define the path for the template directory
const TEMPLATE_DIR: &str = "./templates";
// define the path for the generated site output
const SITE_DIR: &str = "./site";

/// Run (with the) `nyf`.
pub fn run() -> Result<(), &'static str> {
    // read the base html template to a string
    let base = format!("{}/base.html", TEMPLATE_DIR);
    let base_path = Path::new(&base);
    let base_html = fs::read_to_string(base_path)
        .map_err(|_| "couldn't read from templates/base.html; does it exist?")?;

    // read the index html template to a string
    let index = format!("{}/index.html", TEMPLATE_DIR);
    let index_path = Path::new(&index);
    let index_html = fs::read_to_string(index_path)
        .map_err(|_| "couldn't read from templates/index.html; does it exist?")?;

    // create site directory if it doesn't already exist
    let site_dir_path = Path::new(&SITE_DIR);
    if !site_dir_path.is_dir() {
        fs::create_dir(site_dir_path).map_err(|_| "failed to create site output directory")?;
    }

    // integrate the content from the index template into the base template
    let mut index_output = base_html.replace("[[ content ]]", &index_html);
    // set the page title
    index_output = index_output.replace("[[ title ]]", "mycelial technology");
    let index_output_path = format!("{}/index.html", SITE_DIR);
    // write the generated index html to file
    fs::write(index_output_path, index_output.trim())
        .map_err(|_| "failed to write the root index.html file")?;

    // walk the template directory and collect paths for all files and sub-directories
    let template_files: Vec<_> = fs::read_dir(TEMPLATE_DIR)
        .map_err(|_| "failed to read template directory")?
        .map(|res| res.map(|e| e.path()))
        .collect::<Result<Vec<_>, io::Error>>()
        .map_err(|_| "failed to collect template file paths")?;

    // loop through each file and sub-directory
    for entry in template_files {
        if entry.is_dir() {
            // replicate templates sub-directory structure in site output directory
            let template_sub_dir_suffix = entry
                .strip_prefix(TEMPLATE_DIR)
                .map_err(|_| "failed to strip prefix from template directory path")?;
            let site_sub_dir = Path::new(SITE_DIR).join(template_sub_dir_suffix);
            // create the sub-directory if it doesn't already exist
            if !site_sub_dir.is_dir() {
                fs::create_dir(site_sub_dir)
                    .map_err(|_| "failed to create a site output sub-directory")?;
            }
            // read each file from the sub-directory
            for file in fs::read_dir(entry)
                .map_err(|_| "failed to enumerate template sub-directory files")?
            {
                let file = file.map_err(|_| "failed to obtain sub-directory file data")?;
                let file_path = file.path();
                let file_html = fs::read_to_string(&file_path)
                    .map_err(|_| "failed to read template html file to string")?;
                // find the index of the h2 tag (represents the page title)
                let mut title_start = file_html.find("<h2>").ok_or("<h2> tag not found in html")?;
                // increment the index to represent the start of the title text
                title_start += 4;
                // find the index of the h2 closing tag
                let title_end = file_html
                    .find("</h2>")
                    .ok_or("</h2> tag not found in html")?;
                // obtain the title text as a string slice
                let title = &file_html[title_start..title_end];
                // integrate the content from the template into the base template
                let mut file_output = base_html.replace("[[ content ]]", &file_html);
                // integrate the title of the page into the file output
                file_output = file_output.replace("[[ title ]]", title);
                // define the path to which the output html file will be written
                let file_output_path = file_path.to_string_lossy();
                // replace the template directory path with the site output path
                let output_path = file_output_path.replace(TEMPLATE_DIR, SITE_DIR);
                // trim whitespace from the end of the generated html and write to file
                fs::write(output_path, file_output.trim())
                    .map_err(|_| "failed to write html file to site directory")?
            }
        }
    }

    Ok(())
}
