mini-docs 0.3.7

A minimal, secure build-time Markdown to HTML generator for the mini-* family.
Documentation
use std::fs;
use std::thread::sleep;
use std::time::Duration;

use mini_docs::Builder;
use serde_json::Value;

const MTIME_SETTLE: Duration = Duration::from_millis(50);

/// A `.md` file's frontmatter change (here, adding `pinned: true`) must be reflected
/// in `data.json` on the next tick, not just the page's own HTML output.
#[test]
fn tick_regenerates_data_json_when_a_md_files_frontmatter_changes() {
    let input = tempfile::tempdir().expect("create input tempdir");
    let templates = tempfile::tempdir().expect("create templates tempdir");
    let output = tempfile::tempdir().expect("create output tempdir");

    fs::write(input.path().join("a.md"), "# A\n").expect("write a.md");
    fs::write(
        templates.path().join("page.html"),
        "{{ page.content | safe }}",
    )
    .expect("write page.html");

    let builder = Builder::new(input.path())
        .templates(templates.path())
        .output(output.path())
        .default_template("page.html")
        .data_json("data.json");

    let mut watcher = builder.watch().expect("initial watch build should succeed");

    let before: Vec<Value> = serde_json::from_str(
        &fs::read_to_string(output.path().join("data.json")).expect("read data.json"),
    )
    .expect("data.json must be valid JSON");
    assert_eq!(before[0]["pinned"], false);

    sleep(MTIME_SETTLE);
    fs::write(input.path().join("a.md"), "---\npinned: true\n---\n# A\n").expect("rewrite a.md");

    watcher.tick().expect("tick should succeed");

    let after: Vec<Value> = serde_json::from_str(
        &fs::read_to_string(output.path().join("data.json")).expect("read data.json"),
    )
    .expect("data.json must be valid JSON");
    assert_eq!(
        after[0]["pinned"], true,
        "data.json must reflect the frontmatter change: {after:#?}"
    );
}

/// A template-only change must not touch `data.json` at all — its content depends
/// only on frontmatter, not on which template renders a page.
#[test]
fn tick_does_not_rewrite_data_json_on_template_only_change() {
    let input = tempfile::tempdir().expect("create input tempdir");
    let templates = tempfile::tempdir().expect("create templates tempdir");
    let output = tempfile::tempdir().expect("create output tempdir");

    fs::write(input.path().join("a.md"), "# A\n").expect("write a.md");
    fs::write(
        templates.path().join("page.html"),
        "v1: {{ page.content | safe }}",
    )
    .expect("write page.html");

    let builder = Builder::new(input.path())
        .templates(templates.path())
        .output(output.path())
        .default_template("page.html")
        .data_json("data.json");

    let mut watcher = builder.watch().expect("initial watch build should succeed");

    let data_json_mtime_before = fs::metadata(output.path().join("data.json"))
        .expect("stat data.json")
        .modified()
        .expect("mtime of data.json");

    sleep(MTIME_SETTLE);
    fs::write(
        templates.path().join("page.html"),
        "v2: {{ page.content | safe }}",
    )
    .expect("rewrite page.html");

    watcher.tick().expect("tick should succeed");

    let data_json_mtime_after = fs::metadata(output.path().join("data.json"))
        .expect("stat data.json")
        .modified()
        .expect("mtime of data.json");

    assert_eq!(
        data_json_mtime_before, data_json_mtime_after,
        "data.json must not be rewritten on a template-only change"
    );
}