git_worktree_manager/operations/
busy_messages.rs1use crate::operations::busy::{BusyInfo, BusySource};
6
7fn fmt_age(secs: u64) -> String {
8 if secs < 60 {
9 format!("{}s ago", secs)
10 } else if secs < 3600 {
11 format!(
12 "{} minute{} ago",
13 secs / 60,
14 if secs / 60 == 1 { "" } else { "s" }
15 )
16 } else {
17 format!(
18 "{} hour{} ago",
19 secs / 3600,
20 if secs / 3600 == 1 { "" } else { "s" }
21 )
22 }
23}
24
25fn render_hard_section(out: &mut String, hard: &[BusyInfo]) {
26 for h in hard {
27 match h.source {
28 BusySource::ClaudeSession => {
29 out.push_str(" Active Claude session\n");
30 if let Some(secs) = h.started_secs_ago {
31 out.push_str(&format!(" last activity: {}\n", fmt_age(secs)));
32 }
33 if let Some(id_part) = h.cmd.strip_prefix("claude (session ") {
35 let id = id_part.trim_end_matches(')');
36 out.push_str(&format!(" session: {}\n", id));
37 }
38 }
39 BusySource::Lockfile => {
40 out.push_str(&format!(" Lockfile holder: PID {} ({})\n", h.pid, h.cmd));
41 }
42 BusySource::ProcessScan => {
43 out.push_str(&format!(" PID {} {}\n", h.pid, h.cmd));
45 }
46 }
47 out.push('\n');
48 }
49}
50
51fn render_soft_list(out: &mut String, soft: &[BusyInfo]) {
52 for s in soft {
53 let tty_label = match s.tty {
54 Some(true) => "(interactive)",
55 Some(false) => "(no tty)",
56 None => "",
57 };
58 let age_label = match s.started_secs_ago {
59 Some(secs) if secs < 90 => format!(" (started {})", fmt_age(secs)),
60 _ => String::new(),
61 };
62 out.push_str(&format!(
63 " PID {:>6} {} {}{}\n",
64 s.pid, s.cmd, tty_label, age_label
65 ));
66 }
67}
68
69pub fn render_refusal(branch_display: &str, hard: &[BusyInfo], soft: &[BusyInfo]) -> String {
73 let mut out = String::new();
74 match (hard.is_empty(), soft.is_empty()) {
75 (true, true) => return out,
76 (true, false) => {
77 out.push_str(&format!(
78 "⚠ Worktree '{}' may be in use:\n\n",
79 branch_display
80 ));
81 out.push_str(" Processes with cwd in this worktree:\n");
82 render_soft_list(&mut out, soft);
83 out.push('\n');
84 out.push_str(" These may malfunction if the worktree is deleted.\n");
85 out.push_str(" Re-run with --force to delete anyway.\n");
86 }
87 (false, true) => {
88 out.push_str(&format!(
89 "✗ Cannot delete worktree '{}' — in use:\n\n",
90 branch_display
91 ));
92 render_hard_section(&mut out, hard);
93 out.push_str(" Use --force to delete anyway.\n");
94 }
95 (false, false) => {
96 out.push_str(&format!(
97 "✗ Cannot delete worktree '{}' — in use:\n\n",
98 branch_display
99 ));
100 render_hard_section(&mut out, hard);
101 out.push_str(" Additional processes with cwd in this worktree:\n");
102 render_soft_list(&mut out, soft);
103 out.push('\n');
104 out.push_str(" Use --force to delete anyway.\n");
105 }
106 }
107 out
108}