Skip to main content

kaizen/shell/
guidance_candidates.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2//! `kaizen guidance candidates` command helpers.
3
4use crate::guidance::{CandidateAction, CandidateStatus, GuidanceCandidate};
5use crate::shell::cli::workspace_path;
6use crate::store::Store;
7use anyhow::{Result, anyhow};
8use std::fmt::Write;
9use std::path::Path;
10
11pub enum CandidateOp {
12    List { json: bool },
13    Show { id: String, json: bool },
14    Set { id: String, status: CandidateStatus },
15}
16
17pub fn cmd(ws: Option<&Path>, op: CandidateOp) -> Result<()> {
18    print!("{}", text(ws, op)?);
19    Ok(())
20}
21
22pub fn text(ws: Option<&Path>, op: CandidateOp) -> Result<String> {
23    let ws = workspace_path(ws)?;
24    let store = Store::open(&crate::core::workspace::db_path(&ws)?)?;
25    match op {
26        CandidateOp::List { json } => list(&store, json),
27        CandidateOp::Show { id, json } => show(&store, &id, json),
28        CandidateOp::Set { id, status } => set_status(&store, &id, status),
29    }
30}
31
32fn list(store: &Store, json_out: bool) -> Result<String> {
33    let rows = store.list_guidance_candidates()?;
34    if json_out {
35        return Ok(serde_json::to_string_pretty(&rows)?);
36    }
37    Ok(rows.into_iter().map(|c| format_candidate(&c)).collect())
38}
39
40fn show(store: &Store, id: &str, json_out: bool) -> Result<String> {
41    let c = store
42        .get_guidance_candidate(id)?
43        .ok_or_else(|| anyhow!("candidate not found: {id}"))?;
44    if json_out {
45        Ok(serde_json::to_string_pretty(&c)?)
46    } else {
47        Ok(format_candidate(&c))
48    }
49}
50
51fn set_status(store: &Store, id: &str, status: CandidateStatus) -> Result<String> {
52    store.set_guidance_candidate_status(id, status)?;
53    Ok(format!("{} {id}\n", status.as_str()))
54}
55
56pub(crate) fn format_candidate(c: &GuidanceCandidate) -> String {
57    let mut out = String::new();
58    let _ = writeln!(&mut out, "id:       {}", c.id);
59    let _ = writeln!(&mut out, "artifact: {}", c.artifact);
60    let _ = writeln!(&mut out, "status:   {}", c.status.as_str());
61    let _ = writeln!(&mut out, "action:   {}", action_label(&c.action));
62    let _ = writeln!(&mut out, "why:      {}", c.rationale);
63    out
64}
65
66fn action_label(action: &CandidateAction) -> &'static str {
67    match action {
68        CandidateAction::Delete => "delete",
69        CandidateAction::Replace { .. } => "replace",
70        CandidateAction::ReviewOnly => "review_only",
71    }
72}