proc-cli 2.0.2

Semantic CLI tool for process management. Target by port, PID, name or path.
Documentation
//! `proc thaw` - Resume frozen processes with SIGCONT
//!
//! Examples:
//!   proc thaw node              # Resume the frozen processes named node
//!   proc thaw node --cmd        # ... or with node anywhere in their command line
//!   proc thaw :3000             # Resume process on port 3000
//!   proc thaw :3000 :8080       # Resume several targets: separate arguments
//!   proc thaw node --dry-run    # Show what would be resumed
//!   proc thaw :3000 --yes       # Skip confirmation (a port or PID, not a name)
//!
//! Commas still work (`proc thaw :3000,:8080`) but are deprecated.

#[cfg(unix)]
use crate::core::{
    apply_filters, apply_to_each, flatten_targets, resolve_targets_excluding_self, NameMatch,
    Process,
};
use crate::error::{ProcError, Result};
#[cfg(unix)]
use crate::ui::Printer;
use clap::Args;

/// Resume frozen process(es) with SIGCONT
#[derive(Args, Debug)]
pub struct ThawCommand {
    /// Target(s): process name, PID, or :port (separate arguments for several)
    #[arg(required = true, num_args = 1.., value_parser = crate::core::validate_target_arg)]
    pub target: Vec<String>,

    /// Skip confirmation prompt
    #[arg(long, short = 'y')]
    pub yes: bool,

    /// Show what would be resumed without actually resuming
    #[arg(long)]
    pub dry_run: bool,

    /// Output as JSON
    #[arg(long, short = 'j')]
    pub json: bool,

    /// Show verbose output
    #[arg(long, short = 'v')]
    pub verbose: bool,

    /// Filter by directory; takes the next word as the directory unless it starts with '-'
    /// (bare --in is the current directory, --in=DIR is explicit)
    #[arg(long = "in", short = 'i', num_args = 0..=1, default_missing_value = ".")]
    pub in_dir: Option<String>,

    /// Filter by process name
    #[arg(long = "by", short = 'b')]
    pub by_name: Option<String>,

    /// Match name targets and --by as a substring of the process name or full command line (like pkill -f)
    #[arg(long)]
    pub cmd: bool,
}

impl ThawCommand {
    /// Executes the thaw command, resuming frozen processes with SIGCONT.
    #[cfg(unix)]
    pub fn execute(&self) -> Result<()> {
        use nix::sys::signal::Signal;

        let printer = Printer::from_flags(self.json, self.verbose);

        let targets = flatten_targets(&self.target);
        let mode = NameMatch::from_flag(self.cmd);
        let resolved = resolve_targets_excluding_self(&targets, mode);

        if let Some(warning) = resolved.warning() {
            printer.warning(&warning);
        }
        // the JSON results list the targets that matched nothing
        let printer = printer.with_not_found(&resolved.not_found);
        let mut processes = resolved.processes;

        // Apply --in and --by filters
        apply_filters(&mut processes, &self.in_dir, &self.by_name, mode)?;

        // Real CPU readings whenever the list is shown or returned (dry run, the
        // confirmation prompt, or the error for a missing `--yes`); only `--yes`
        // acts at once, without the wait
        if self.dry_run || !self.yes {
            Process::measure_cpu(&mut processes);
        }

        if processes.is_empty() {
            return Err(ProcError::ProcessNotFound(self.target.join(" ")));
        }

        if self.dry_run {
            printer.print_dry_run("thaw", &processes);
            return Ok(());
        }

        if !printer.ask_confirm("thaw", &processes, self.yes)? {
            return Ok(());
        }

        let (succeeded, failed) =
            apply_to_each(&processes, |proc| proc.send_signal(Signal::SIGCONT));

        printer.print_action_result("thaw", &succeeded, &failed)
    }

    /// Windows stub
    #[cfg(not(unix))]
    pub fn execute(&self) -> Result<()> {
        Err(ProcError::NotSupported(
            "thaw (SIGCONT) is not supported on Windows".to_string(),
        ))
    }
}