Skip to main content

gn_cli/commands/
add.rs

1use anyhow::{Context, Result};
2use clap::Args;
3use gn_core::{Note, NotesEngine};
4use std::process::Command;
5
6#[derive(Args)]
7pub struct AddArgs {
8    /// File to attach the note to
9    #[arg(short, long)]
10    pub file: String,
11
12    /// Line range (e.g., "10" or "10-15")
13    #[arg(short, long)]
14    pub line: String,
15
16    /// Note message
17    #[arg(short, long)]
18    pub message: String,
19
20    /// Namespace to add the note to
21    #[arg(short, long, default_value = "comments")]
22    pub namespace: String,
23
24    /// Thread parent note ID (for replies)
25    #[arg(short, long)]
26    pub thread: Option<String>,
27}
28
29pub fn run(args: &AddArgs) -> Result<()> {
30    let name_output = Command::new("git")
31        .args(["config", "user.name"])
32        .output()
33        .context("Failed to read user.name")?;
34    let email_output = Command::new("git")
35        .args(["config", "user.email"])
36        .output()
37        .context("Failed to read user.email")?;
38
39    let author = format!(
40        "{} <{}>",
41        String::from_utf8_lossy(&name_output.stdout).trim(),
42        String::from_utf8_lossy(&email_output.stdout).trim()
43    );
44
45    let head_output = Command::new("git")
46        .args(["rev-parse", "HEAD"])
47        .output()
48        .context("Failed to get HEAD commit")?;
49    let commit = String::from_utf8_lossy(&head_output.stdout)
50        .trim()
51        .to_string();
52
53    let mut line_start = None;
54    let mut line_end = None;
55    if !args.line.is_empty() {
56        if args.line.contains('-') {
57            let parts: Vec<&str> = args.line.split('-').collect();
58            if parts.len() == 2 {
59                line_start = parts[0].parse().ok();
60                line_end = parts[1].parse().ok();
61            }
62        } else {
63            line_start = args.line.parse().ok();
64            line_end = line_start;
65        }
66    }
67
68    let namespace = gn_core::Namespace::Custom(args.namespace.clone());
69
70    let mut note = Note::new(
71        commit,
72        Some(args.file.clone()),
73        line_start,
74        line_end,
75        args.message.clone(),
76        author,
77        namespace,
78    );
79
80    if let Some(thread_str) = &args.thread {
81        if let Ok(tid) = uuid::Uuid::parse_str(thread_str) {
82            note.thread_id = Some(tid);
83        }
84    }
85
86    let engine = NotesEngine::new(".");
87    engine.write_note(&note)?;
88
89    println!("✓ Note {} added to refs/notes/{}", note.id, args.namespace);
90
91    Ok(())
92}