gn_cli/commands/
resolve.rs1use anyhow::{anyhow, Result};
2use clap::Args;
3use gn_core::{NoteStatus, NotesEngine};
4
5#[derive(Args)]
6pub struct ResolveArgs {
7 pub id: Option<String>,
9
10 #[arg(short, long, default_value = "resolved")]
12 pub status: String,
13}
14
15pub fn run(args: &ResolveArgs) -> Result<()> {
16 let engine = NotesEngine::new(".");
17 let namespaces = vec!["comments", "review", "todos"];
18
19 let mut all_notes = Vec::new();
20
21 for ns in &namespaces {
22 let namespace_enum = gn_core::Namespace::Custom(ns.to_string());
23 if let Ok(notes) = engine.read_notes(&namespace_enum) {
24 all_notes.extend(notes);
25 }
26 }
27
28 if all_notes.is_empty() {
29 return Err(anyhow!("No notes found in repository."));
30 }
31
32 let found_note = match &args.id {
34 None => match super::picker::pick_note("Select note to resolve:", &all_notes)? {
35 Some(n) => Some(n.clone()),
36 None => {
37 println!("Cancelled.");
38 return Ok(());
39 }
40 },
41 Some(raw_id) => {
42 let target = raw_id.trim().trim_start_matches('#');
43 if target.eq_ignore_ascii_case("latest") || target == "^" {
44 all_notes.last().cloned()
45 } else if let Ok(idx) = target.parse::<usize>() {
46 if idx >= 1 && idx <= all_notes.len() {
47 Some(all_notes[idx - 1].clone())
48 } else {
49 None
50 }
51 } else {
52 all_notes
53 .iter()
54 .find(|n| n.id.to_string().starts_with(target))
55 .cloned()
56 }
57 }
58 };
59
60 let mut note = found_note.ok_or_else(|| anyhow!("Target note not found"))?;
61
62 let status = match args.status.to_lowercase().as_str() {
63 "approved" => NoteStatus::Approved,
64 "rejected" => NoteStatus::Rejected,
65 "resolved" => NoteStatus::Resolved,
66 "open" => NoteStatus::Open,
67 _ => {
68 return Err(anyhow!(
69 "Invalid status. Use approved, rejected, resolved, or open."
70 ))
71 }
72 };
73
74 note.status = status;
75 engine.write_note(¬e)?;
76
77 println!(
78 "\x1b[32m✔\x1b[0m Note \x1b[36m{}\x1b[0m marked as \x1b[1m{:?}\x1b[0m",
79 ¬e.id.to_string()[..8],
80 note.status
81 );
82
83 Ok(())
84}