use anyhow::Result;
use comfy_table::{presets::UTF8_FULL, Cell, ContentArrangement, Table};
use std::path::PathBuf;
use crate::cli::ListArgs;
use crate::model::{Kind, Priority, Project, Requirement, Status};
use crate::storage::load_resolved;
pub fn run(args: ListArgs, file: &Option<PathBuf>) -> Result<()> {
let (_, project) = load_resolved(file)?;
let filtered = filter(&project, &args);
if args.json {
let refs: Vec<&Requirement> = filtered;
println!("{}", serde_json::to_string_pretty(&refs)?);
return Ok(());
}
let mut table = Table::new();
table
.load_preset(UTF8_FULL)
.set_content_arrangement(ContentArrangement::Dynamic)
.set_header(vec!["ID", "Title", "Kind", "Pri", "Status", "Tags"]);
for r in filtered {
table.add_row(vec![
Cell::new(&r.id),
Cell::new(truncate(&r.title, 60)),
Cell::new(r.kind.as_str()),
Cell::new(r.priority.as_str()),
Cell::new(r.status.as_str()),
Cell::new(r.tags.join(", ")),
]);
}
if table.row_count() == 0 {
println!("(no requirements match)");
} else {
println!("{table}");
}
Ok(())
}
fn truncate(s: &str, n: usize) -> String {
if s.chars().count() <= n {
s.to_string()
} else {
let mut out: String = s.chars().take(n - 1).collect();
out.push('…');
out
}
}
pub fn filter<'a>(project: &'a Project, args: &ListArgs) -> Vec<&'a Requirement> {
let kind: Option<Kind> = args.kind.map(Into::into);
let priority: Option<Priority> = args.priority.map(Into::into);
let status: Option<Status> = args.status.map(Into::into);
let q = args.query.as_deref().map(str::to_lowercase);
let hide_obsolete = !args.include_obsolete && !matches!(status, Some(Status::Obsolete));
project
.requirements
.values()
.filter(|r| !(hide_obsolete && matches!(r.status, Status::Obsolete)))
.filter(|r| kind.is_none_or(|k| r.kind == k))
.filter(|r| priority.is_none_or(|p| r.priority == p))
.filter(|r| status.is_none_or(|s| r.status == s))
.filter(|r| args.tag.iter().all(|t| r.tags.iter().any(|rt| rt == t)))
.filter(|r| match &q {
None => true,
Some(needle) => {
r.title.to_lowercase().contains(needle)
|| r.statement.to_lowercase().contains(needle)
}
})
.collect()
}