mini-docs 0.4.5

A minimal, secure build-time Markdown to HTML generator for the mini-* family.
Documentation
use serde_json::{Map, Value};

/// Returns `true` if this page's frontmatter marks it as a draft (`draft: true`) —
/// excluded from both the HTML build output and the `data.json` index entirely.
///
/// Marking an already-published page `draft: true` does not retroactively delete its
/// existing `.html` output — `build()` has no general orphan-removal pass (deleting a
/// `.md` file doesn't clean up its old output either); this is a pre-existing,
/// documented limitation, not draft-specific.
pub(crate) fn is_draft(frontmatter: &Value) -> bool {
    frontmatter
        .get("draft")
        .and_then(Value::as_bool)
        .unwrap_or(false)
}

/// Builds one `data.json` entry for a page.
///
/// `date`, `updated`, `version`, and `summary` default to `""` when the frontmatter
/// key is absent (or isn't a string); `tags` defaults to `[]`, silently dropping any
/// non-string list item; `pinned` defaults to `false`. Field order here matches a
/// hand-authored `data.json` for readability, but `serde_json::Map`'s default
/// (non-`preserve_order`) serialization is alphabetical regardless — that ordering is
/// cosmetic intent, not a guaranteed wire format.
pub(crate) fn page_entry(id: &str, title: &str, url: &str, frontmatter: &Value) -> Value {
    let string_field = |key: &str| -> String {
        frontmatter
            .get(key)
            .and_then(Value::as_str)
            .unwrap_or("")
            .to_string()
    };

    let tags: Vec<Value> = frontmatter
        .get("tags")
        .and_then(Value::as_array)
        .map(|items| {
            items
                .iter()
                .filter_map(Value::as_str)
                .map(|s| Value::String(s.to_string()))
                .collect()
        })
        .unwrap_or_default();

    let pinned = frontmatter
        .get("pinned")
        .and_then(Value::as_bool)
        .unwrap_or(false);

    let mut entry = Map::new();
    entry.insert("id".to_string(), Value::String(id.to_string()));
    entry.insert("title".to_string(), Value::String(title.to_string()));
    entry.insert("date".to_string(), Value::String(string_field("date")));
    entry.insert(
        "updated".to_string(),
        Value::String(string_field("updated")),
    );
    entry.insert(
        "version".to_string(),
        Value::String(string_field("version")),
    );
    entry.insert("url".to_string(), Value::String(url.to_string()));
    entry.insert(
        "summary".to_string(),
        Value::String(string_field("summary")),
    );
    entry.insert("tags".to_string(), Value::Array(tags));
    entry.insert("pinned".to_string(), Value::Bool(pinned));
    Value::Object(entry)
}

#[cfg(test)]
#[path = "../tests/unit/data_json.rs"]
mod tests;