kaizen/shell/
guidance_candidates.rs1use 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}