proc_cli/commands/
thaw.rs1use crate::core::{parse_targets, resolve_in_dir, resolve_targets_excluding_self};
10use crate::error::{ProcError, Result};
11use crate::ui::{OutputFormat, Printer};
12use clap::Args;
13use dialoguer::Confirm;
14use std::path::PathBuf;
15
16#[derive(Args, Debug)]
18pub struct ThawCommand {
19 #[arg(required = true)]
21 pub target: String,
22
23 #[arg(long, short = 'y')]
25 pub yes: bool,
26
27 #[arg(long)]
29 pub dry_run: bool,
30
31 #[arg(long, short = 'j')]
33 pub json: bool,
34
35 #[arg(long, short = 'v')]
37 pub verbose: bool,
38
39 #[arg(long = "in", short = 'i', num_args = 0..=1, default_missing_value = ".")]
41 pub in_dir: Option<String>,
42
43 #[arg(long = "by", short = 'b')]
45 pub by_name: Option<String>,
46}
47
48impl ThawCommand {
49 #[cfg(unix)]
51 pub fn execute(&self) -> Result<()> {
52 use nix::sys::signal::Signal;
53
54 let format = if self.json {
55 OutputFormat::Json
56 } else {
57 OutputFormat::Human
58 };
59 let printer = Printer::new(format, self.verbose);
60
61 let targets = parse_targets(&self.target);
62 let (mut processes, not_found) = resolve_targets_excluding_self(&targets);
63
64 if !not_found.is_empty() {
65 printer.warning(&format!("Not found: {}", not_found.join(", ")));
66 }
67
68 let in_dir_filter = resolve_in_dir(&self.in_dir);
70 processes.retain(|p| {
71 if let Some(ref dir_path) = in_dir_filter {
72 if let Some(ref cwd) = p.cwd {
73 if !PathBuf::from(cwd).starts_with(dir_path) {
74 return false;
75 }
76 } else {
77 return false;
78 }
79 }
80 if let Some(ref name) = self.by_name {
81 if !p.name.to_lowercase().contains(&name.to_lowercase()) {
82 return false;
83 }
84 }
85 true
86 });
87
88 if processes.is_empty() {
89 return Err(ProcError::ProcessNotFound(self.target.clone()));
90 }
91
92 if self.dry_run {
93 printer.print_processes(&processes);
94 printer.warning(&format!(
95 "Dry run: would resume {} process{}",
96 processes.len(),
97 if processes.len() == 1 { "" } else { "es" }
98 ));
99 return Ok(());
100 }
101
102 if !self.yes && !self.json {
103 printer.print_confirmation("resume", &processes);
104
105 let prompt = format!(
106 "Resume {} process{}?",
107 processes.len(),
108 if processes.len() == 1 { "" } else { "es" }
109 );
110
111 if !Confirm::new()
112 .with_prompt(prompt)
113 .default(false)
114 .interact()?
115 {
116 printer.warning("Aborted");
117 return Ok(());
118 }
119 }
120
121 let mut succeeded = Vec::new();
122 let mut failed = Vec::new();
123
124 for proc in &processes {
125 match proc.send_signal(Signal::SIGCONT) {
126 Ok(()) => succeeded.push(proc.clone()),
127 Err(e) => failed.push((proc.clone(), e.to_string())),
128 }
129 }
130
131 printer.print_action_result("Resumed", &succeeded, &failed);
132
133 Ok(())
134 }
135
136 #[cfg(not(unix))]
138 pub fn execute(&self) -> Result<()> {
139 Err(ProcError::NotSupported(
140 "thaw (SIGCONT) is not supported on Windows".to_string(),
141 ))
142 }
143}