git_adr/cli/
link.rs

1//! Link ADR to commits.
2
3use anyhow::Result;
4use clap::Args as ClapArgs;
5use colored::Colorize;
6
7use crate::core::{ConfigManager, Git, NotesManager};
8
9/// Arguments for the link command.
10#[derive(ClapArgs, Debug)]
11pub struct Args {
12    /// ADR ID.
13    pub adr_id: String,
14
15    /// Commit SHA to link.
16    pub commit: String,
17}
18
19/// Run the link command.
20///
21/// # Errors
22///
23/// Returns an error if linking fails.
24pub fn run(args: Args) -> Result<()> {
25    let git = Git::new();
26    git.check_repository()?;
27
28    let config = ConfigManager::new(git.clone()).load()?;
29    let notes = NotesManager::new(git.clone(), config);
30
31    // Find the ADR
32    let adrs = notes.list()?;
33    let adr = adrs
34        .into_iter()
35        .find(|a| a.id == args.adr_id || a.id.contains(&args.adr_id))
36        .ok_or_else(|| anyhow::anyhow!("ADR not found: {}", args.adr_id))?;
37
38    eprintln!(
39        "{} Linking ADR {} to commit {}",
40        "→".blue(),
41        adr.id.cyan(),
42        &args.commit[..8.min(args.commit.len())].yellow()
43    );
44
45    // Verify the target commit exists
46    let full_commit = git.run_output(&["rev-parse", &args.commit])?;
47    let full_commit = full_commit.trim().to_string();
48
49    if adr.commit == full_commit {
50        eprintln!("{} ADR is already linked to this commit", "!".yellow());
51        return Ok(());
52    }
53
54    // Remove note from old commit
55    notes.delete(&adr.id)?;
56
57    // Create note on new commit
58    let mut new_adr = adr.clone();
59    new_adr.commit.clone_from(&full_commit);
60    notes.create(&new_adr)?;
61
62    eprintln!(
63        "{} ADR {} moved from {} to {}",
64        "✓".green(),
65        adr.id,
66        &adr.commit[..8],
67        &full_commit[..8]
68    );
69
70    Ok(())
71}