cartulary 0.3.0-alpha.1

The knowledge layer of your project — decisions, issues, docs, all in one place.
Documentation
//! `cartu man` — render a user-facing concept page to the terminal.
//!
//! Without an argument, lists the available topics. With a `<name>`,
//! renders that topic via the [`show`] use case and the terminal
//! renderer adapter.
//!
//! [`show`]: crate::domain::usecases::doc::show::show

use clap::ArgMatches;

use crate::domain::usecases::doc::show::show;
use crate::infra::driven::doc::links::terminal::TerminalLinkResolver;
use crate::infra::driven::doc::registry_source::RegistryDocSource;
use crate::infra::driven::doc::render::terminal::TerminalRenderer;
use crate::infra::driving::cli::errors::{die1, CliError};
use crate::infra::driving::cli::theme;
use crate::infra::driving::cli::OutputFormat;

pub(in super::super) fn execute_man(matches: &ArgMatches, output_fmt: OutputFormat) {
    let source = RegistryDocSource;

    let Some(name) = matches.get_one::<String>("name") else {
        print_topic_listing(&source);
        return;
    };

    let renderer = TerminalRenderer::new(TerminalLinkResolver);
    match show(name, &source, &renderer) {
        Ok(rendered) => print!("{rendered}"),
        Err(err) => die1(CliError::new(err.to_string()).kind("not-found"), output_fmt),
    }
}

fn print_topic_listing(source: &RegistryDocSource) {
    let summaries = source.summaries();
    let max_name = summaries.iter().map(|(n, _)| n.len()).max().unwrap_or(0);
    println!("{}", theme::section("Available concepts"));
    for (name, summary) in &summaries {
        println!("  {name:<max_name$}  {summary}");
    }
    println!();
    println!("Run `cartu man <name>` to view a concept.");
}