nestrs-cli-rs 0.1.0

Rust port of the Nest CLI for the nestrs organization.
Documentation
//! Upstream source: `../nest-cli/lib/utils/tree-kill.ts`.

use std::io;
use std::process::Command;

pub fn tree_kill_sync(pid: u32, _signal: Option<&str>) -> io::Result<()> {
    #[cfg(windows)]
    {
        let status = Command::new("taskkill")
            .args(["/pid", &pid.to_string(), "/T", "/F"])
            .status()?;
        return status.success().then_some(()).ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::Other,
                format!("taskkill failed for pid {pid}"),
            )
        });
    }

    #[cfg(not(windows))]
    {
        for child_pid in get_all_childs(pid)? {
            kill_pid(child_pid, _signal)?;
        }
        kill_pid(pid, _signal)
    }
}

#[cfg(not(windows))]
fn get_all_pid() -> io::Result<Vec<(u32, u32)>> {
    let output = Command::new("ps").args(["-A", "-o", "pid,ppid"]).output()?;
    if !output.status.success() {
        return Ok(Vec::new());
    }

    let stdout = String::from_utf8_lossy(&output.stdout);
    Ok(stdout
        .lines()
        .skip(1)
        .filter_map(|line| {
            let mut parts = line.split_whitespace();
            let pid = parts.next()?.parse::<u32>().ok()?;
            let ppid = parts.next()?.parse::<u32>().ok()?;
            Some((pid, ppid))
        })
        .collect())
}

#[cfg(not(windows))]
fn get_all_childs(pid: u32) -> io::Result<Vec<u32>> {
    let all_pid = get_all_pid()?;
    let mut result = Vec::new();
    collect_childs(pid, &all_pid, &mut result);
    Ok(result)
}

#[cfg(not(windows))]
fn collect_childs(pid: u32, all_pid: &[(u32, u32)], result: &mut Vec<u32>) {
    for (child_pid, ppid) in all_pid.iter().copied().filter(|(_, ppid)| *ppid == pid) {
        result.push(child_pid);
        collect_childs(child_pid, all_pid, result);
    }
}

#[cfg(not(windows))]
fn kill_pid(pid: u32, signal: Option<&str>) -> io::Result<()> {
    let signal = signal.unwrap_or("TERM").trim_start_matches("SIG");
    let status = Command::new("kill")
        .args([format!("-{signal}"), pid.to_string()])
        .status()?;

    if status.success() {
        Ok(())
    } else {
        Err(io::Error::new(
            io::ErrorKind::Other,
            format!("kill failed for pid {pid}"),
        ))
    }
}