poet 0.7.0

Static site generator with optional MCP server and LLM SEO optimizations
Documentation
use anyhow::Result;
use markdown::mdast::Node;
use markdown::mdast::Root;
use markdown::mdast::Toml;
use serde::de::DeserializeOwned;

pub fn find_front_matter_in_mdast<TFrontMatter: DeserializeOwned>(
    mdast: &Node,
) -> Result<Option<TFrontMatter>> {
    match mdast {
        Node::Root(Root { children, .. }) => {
            for child in children {
                if let Some(front_matter) = find_front_matter_in_mdast::<TFrontMatter>(child)? {
                    return Ok(Some(front_matter));
                }
            }

            Ok(None)
        }
        Node::Toml(Toml { value, .. }) => Ok(Some(toml::from_str::<TFrontMatter>(value)?)),
        _ => Ok(None),
    }
}

#[cfg(test)]
mod tests {
    use anyhow::Result;
    use serde::Deserialize;

    use super::*;
    use crate::string_to_mdast::string_to_mdast;

    #[derive(Deserialize)]
    struct TestFrontMatter {
        title: String,
    }

    #[test]
    fn extracts_toml_front_matter_from_document() -> Result<()> {
        let mdast = string_to_mdast("+++\ntitle = \"Hello\"\n+++\n\nBody text")?;
        let front_matter: Option<TestFrontMatter> = find_front_matter_in_mdast(&mdast)?;

        assert_eq!(
            front_matter.map(|front_matter| front_matter.title),
            Some("Hello".to_string())
        );

        Ok(())
    }

    #[test]
    fn returns_none_when_document_has_no_front_matter() -> Result<()> {
        let mdast = string_to_mdast("Just body text")?;
        let front_matter: Option<TestFrontMatter> = find_front_matter_in_mdast(&mdast)?;

        assert!(front_matter.is_none());

        Ok(())
    }
}