cartulary 0.3.0-alpha.1

The knowledge layer of your project — decisions, issues, docs, all in one place.
Documentation
use clap::ArgMatches;

use crate::domain::model::search::SearchFilter;
use crate::domain::usecases::search::search_records;
use crate::infra::driven::fs::search_repository::NucleoSearchRepository;
use crate::infra::driving::cli::errors::{die1, CliError};
use crate::infra::driving::cli::theme;
use crate::infra::driving::cli::Context;

pub(in super::super) fn execute_search(matches: &ArgMatches, ctx: &Context<'_>) {
    let query = crate::infra::driving::cli::helpers::required_str(matches, "query");

    let mut filter = SearchFilter::new();

    if let Some(kind) = matches.get_one::<String>("kind") {
        filter = filter.kind(kind.as_str());
    }

    if let Some(limit_str) = matches.get_one::<String>("limit") {
        if let Ok(n) = limit_str.parse::<usize>() {
            filter = filter.limit(n);
        }
    }

    let repo = NucleoSearchRepository {
        issues_dir: ctx.issues_dir().to_path_buf(),
        decision_kinds: ctx
            .config()
            .decision_kinds
            .iter()
            .map(|k| (k.kind.clone(), k.dir.clone()))
            .collect(),
        schema_version: ctx.config().schema_version,
    };

    let hits = search_records(&repo, query, &filter).unwrap_or_else(|e| {
        die1(CliError::new(format!("search failed: {e}")), ctx.output_fmt);
    });

    if ctx.output_fmt.is_structured() {
        crate::infra::driving::cli::render_structured(&hits, ctx.output_fmt);
        return;
    }

    if hits.is_empty() {
        println!("no results for {query:?}");
        return;
    }

    for hit in &hits {
        println!(
            "{}  {}  {}",
            theme::id(&hit.id.to_string()),
            theme::label(hit.kind.as_str()),
            hit.title.as_str(),
        );
        if let Some(excerpt) = &hit.excerpt {
            println!("  {excerpt}");
        }
    }
}