poet 0.7.0

Static site generator with optional MCP server and LLM SEO optimizations
Documentation
use std::collections::BTreeMap;

use rhai::CustomType;
use rhai::TypeBuilder;

use crate::author::Author;
use crate::author_basename::AuthorBasename;
use crate::author_resolve_result::AuthorResolveResult;

#[derive(Clone, Default)]
pub struct AuthorCollection {
    authors: BTreeMap<AuthorBasename, Author>,
}

impl AuthorCollection {
    pub fn insert(&mut self, basename: AuthorBasename, author: Author) {
        self.authors.insert(basename, author);
    }

    pub fn values(&self) -> impl Iterator<Item = &Author> {
        self.authors.values()
    }

    pub fn resolve(&self, names: &[String]) -> AuthorResolveResult {
        let mut found_authors = Vec::new();
        let mut missing_authors = Vec::new();

        for name in names {
            let basename = AuthorBasename::from(name.clone());

            match self.authors.get(&basename) {
                Some(author) => found_authors.push(author.clone()),
                None => missing_authors.push(name.clone()),
            }
        }

        AuthorResolveResult {
            found_authors,
            missing_authors,
        }
    }
}

impl CustomType for AuthorCollection {
    fn build(mut builder: TypeBuilder<Self>) {
        builder.with_name("AuthorCollection");
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::author_data::AuthorData;

    fn author(basename: &str) -> Author {
        Author {
            basename: basename.to_string().into(),
            data: AuthorData::mock(basename),
        }
    }

    fn collection() -> AuthorCollection {
        let mut collection = AuthorCollection::default();

        collection.insert("alice".to_string().into(), author("alice"));
        collection.insert("bob".to_string().into(), author("bob"));

        collection
    }

    #[test]
    fn resolve_separates_found_and_missing_authors() {
        let result = collection().resolve(&["alice".to_string(), "carol".to_string()]);

        assert_eq!(result.found_authors.len(), 1);
        assert_eq!(result.found_authors[0].basename, "alice".to_string().into());
        assert_eq!(result.missing_authors, vec!["carol".to_string()]);
    }

    #[test]
    fn resolve_returns_empty_results_for_empty_input() {
        let result = collection().resolve(&[]);

        assert!(result.found_authors.is_empty());
        assert!(result.missing_authors.is_empty());
    }
}