cartulary 0.3.0-alpha.1

The knowledge layer of your project — decisions, issues, docs, all in one place.
Documentation
//! `DocSource` adapter over the static concept [`REGISTRY`].
//!
//! Constructed at the composition root and passed to the [`show`] use case.
//!
//! [`show`]: crate::domain::usecases::doc::show::show
//! [`REGISTRY`]: crate::domain::doc::REGISTRY

use crate::domain::doc::{split, REGISTRY};
use crate::domain::usecases::doc::show::DocSource;

pub struct RegistryDocSource;

impl RegistryDocSource {
    /// Sorted list of `(name, summary)` pairs used by `cartu man`
    /// (no argument). Topics whose source carries no summary are
    /// returned with an empty string — the registry test forbids
    /// that case at build time, so it should not occur in practice.
    pub fn summaries(&self) -> Vec<(String, String)> {
        let mut s: Vec<(String, String)> = REGISTRY
            .iter()
            .map(|c| {
                let summary = split(c.source).0.unwrap_or("").to_string();
                (c.name.to_string(), summary)
            })
            .collect();
        s.sort_by(|a, b| a.0.cmp(&b.0));
        s
    }
}

impl DocSource for RegistryDocSource {
    fn lookup(&self, name: &str) -> Option<&str> {
        REGISTRY
            .iter()
            .find(|c| c.name == name)
            .map(|c| split(c.source).1)
    }

    fn topics(&self) -> Vec<String> {
        let mut t: Vec<String> = REGISTRY.iter().map(|c| c.name.to_string()).collect();
        t.sort();
        t
    }
}

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

    #[test]
    fn topics_returns_sorted_names() {
        let topics = RegistryDocSource.topics();
        let mut sorted = topics.clone();
        sorted.sort();
        assert_eq!(topics, sorted);
    }

    #[test]
    fn lookup_known_concept_returns_body_without_frontmatter() {
        let src = RegistryDocSource.lookup("issue").unwrap();
        assert!(src.starts_with("# Issue"));
        assert!(!src.contains("summary:"));
        assert!(!src.starts_with("---"));
    }

    #[test]
    fn lookup_unknown_concept_returns_none() {
        assert!(RegistryDocSource.lookup("nope").is_none());
    }

    #[test]
    fn summaries_returns_sorted_pairs() {
        let s = RegistryDocSource.summaries();
        let names: Vec<&str> = s.iter().map(|(n, _)| n.as_str()).collect();
        let mut sorted = names.clone();
        sorted.sort();
        assert_eq!(names, sorted);
        assert!(s.iter().all(|(_, summary)| !summary.is_empty()));
    }
}