cartulary 0.3.0-alpha.1

The knowledge layer of your project — decisions, issues, docs, all in one place.
Documentation
//! `TemplateEngine` port — the seam between the pure site-build use case
//! and a concrete templating implementation (Tera, in-process today).
//!
//! Domain code constructs a [`TemplateContext`] from any
//! `serde::Serialize` shape, then asks the engine to render a named
//! template against it. The trait is intentionally narrow: template
//! lookup conventions (e.g. `<kind>/single.html` → `_default/single.html`)
//! live in the caller, not in the engine.

use serde::Serialize;

/// Generic, serde-compatible bag of values fed to the template engine.
///
/// We intentionally use `serde_json::Value` as the carrier rather than a
/// bespoke enum so the domain stays free of any templating-engine type
/// while still being able to express arbitrary nested structures (lists of
/// records, optional fields, link maps, …).
#[derive(Debug, Clone)]
pub struct TemplateContext(pub serde_json::Value);

impl TemplateContext {
    /// Empty context — useful for templates that have no variables.
    pub fn new() -> Self {
        Self(serde_json::Value::Object(serde_json::Map::new()))
    }

    /// Build a context from any serializable value. Typically a struct
    /// representing the page-level data passed to a single render.
    pub fn from<T: Serialize>(value: &T) -> anyhow::Result<Self> {
        Ok(Self(serde_json::to_value(value)?))
    }
}

impl Default for TemplateContext {
    fn default() -> Self {
        Self::new()
    }
}

/// Render a named template with a context, returning HTML.
///
/// Errors must surface enough information for the caller to point the user
/// at the offending template — typically the template name plus the parse
/// or rendering message.
pub trait TemplateEngine {
    fn render(&self, name: &str, ctx: &TemplateContext) -> anyhow::Result<String>;
}