lyt 0.1.1

A static site generator written in Rust
Documentation
use std::fs;
use std::path::PathBuf;

use crate::markdown::parse_markdown;
use clap::Parser;
use handlebars::handlebars_helper;
use serde_json::json;
use toml::value::Datetime;
use walkdir::WalkDir;

handlebars_helper!(format_timestamp: |timestamp: Datetime| {
    crate::toml::format_datetime(timestamp)
});

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct Args {
    /// The input directory
    #[arg(short, long, default_value = ".")]
    pub input_dir: String,

    /// The output directory
    #[arg(short, long, default_value = "./dist")]
    pub output_dir: String,

    /// Verbose output
    #[arg(short, long, default_value = "false")]
    pub verbose: bool,

    /// Watch for changes and rebuild
    #[arg(short, long, default_value = "false")]
    pub watch: bool,
}

pub struct Paths {
    pub input_dir: PathBuf,
    pub output_dir: PathBuf,

    pub pages_path: PathBuf,
    pub templates_path: PathBuf,
}

impl Paths {
    pub fn new(input_dir: &str, output_dir: &str) -> Self {
        let input_dir = PathBuf::from(input_dir);
        let output_dir = PathBuf::from(output_dir);

        let pages_path = input_dir.join("pages");
        let templates_path = input_dir.join("templates");

        Self {
            input_dir,
            output_dir,
            pages_path,
            templates_path,
        }
    }
}

pub fn build_handlebars_stack(templates_path: &PathBuf) -> handlebars::Handlebars<'static> {
    let mut handlebars = handlebars::Handlebars::new();

    for entry in WalkDir::new(templates_path)
        .into_iter()
        .filter_map(|e| e.ok())
    {
        if entry.file_type().is_file() {
            let file_name = entry.path().file_stem().unwrap().to_str().unwrap();
            let tpl_path = entry.path().to_str().unwrap();
            handlebars
                .register_template_file(file_name, tpl_path)
                .expect("Should have been able to register the template");
        }
    }

    handlebars.register_helper("format_timestamp", Box::new(format_timestamp));

    handlebars
}

pub fn build_pages(
    verbose: bool,
    pages_path: &PathBuf,
    output_dir: &PathBuf,
    templates_path: &PathBuf,
) {
    let handlebars = build_handlebars_stack(templates_path);

    for entry in WalkDir::new(pages_path).into_iter().filter_map(|e| e.ok()) {
        if entry.file_type().is_file() {
            if verbose {
                println!(" input: {}", entry.path().display());
            }

            let contents =
                fs::read_to_string(entry.path()).expect("Should have been able to read the file");

            let (attrs, html_output) =
                parse_markdown(contents.as_str(), "Solarized (dark)".to_string());

            let data = json!({
                "title": attrs.title,
                "author": attrs.author,
                "timestamp": attrs.timestamp,
                "content": html_output,
            });

            let output = handlebars
                .render("page", &data)
                .expect("Should have been able to render the template");

            let mut slug = entry.path().file_stem().unwrap().to_str().unwrap();
            if slug == "index" {
                slug = "";
            }

            let output_path = output_dir.join(slug).join("index.html");

            if verbose {
                println!("output: {}", output_path.display());
            }
            let prefix = output_path.parent().unwrap();
            fs::create_dir_all(prefix).expect("Should have been able to create the directory");

            fs::write(output_path, output).expect("Should have been able to write the file");
        }
    }
}