sal/src/process/README.md
timurgordon 65e404e517
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
merge branches and document
2025-05-23 21:12:17 +03:00

6.1 KiB

SAL Process Module (sal::process)

The process module in the SAL (System Abstraction Layer) library provides a robust and cross-platform interface for creating, managing, and interacting with system processes. It is divided into two main sub-modules: run for command and script execution, and mgmt for process management tasks like listing, finding, and terminating processes.

Core Functionalities

1. Command and Script Execution (run.rs)

The run.rs sub-module offers flexible ways to execute external commands and multi-line scripts.

RunBuilder

The primary interface for execution is the RunBuilder, obtained via sal::process::run("your_command_or_script"). It allows for fluent configuration:

  • .die(bool): If true (default), an error is returned if the command fails. If false, a CommandResult with success: false is returned instead.
  • .silent(bool): If true (default is false), suppresses stdout and stderr from being printed to the console during execution. Output is still captured in CommandResult.
  • .async_exec(bool): If true (default is false), executes the command or script in a separate thread, returning an immediate placeholder CommandResult.
  • .log(bool): If true (default is false), prints a log message before executing the command.
  • .execute() -> Result<CommandResult, RunError>: Executes the configured command or script.

Input Handling:

  • Single-line commands: Treated as a command and its arguments (e.g., "ls -la").
  • Multi-line scripts: If the input string contains newline characters (\n), it's treated as a script.
    • The script content is automatically dedented.
    • On Unix-like systems, #!/bin/bash -e is prepended (if no shebang exists) to ensure the script exits on error.
    • A temporary script file is created, made executable, and then run.

CommandResult

All execution functions return a Result<CommandResult, RunError>. The CommandResult struct contains:

  • stdout: String: Captured standard output.
  • stderr: String: Captured standard error.
  • success: bool: true if the command exited with a zero status code.
  • code: i32: The exit code of the command.

Convenience Functions:

  • sal::process::run_command("cmd_or_script"): Equivalent to run("cmd_or_script").execute().
  • sal::process::run_silent("cmd_or_script"): Equivalent to run("cmd_or_script").silent(true).execute().

Error Handling:

  • RunError: Enum for errors specific to command/script execution (e.g., EmptyCommand, CommandExecutionFailed, ScriptPreparationFailed).

2. Process Management (mgmt.rs)

The mgmt.rs sub-module provides tools for querying and managing system processes.

ProcessInfo

A struct holding basic process information:

  • pid: i64
  • name: String
  • memory: f64 (currently a placeholder)
  • cpu: f64 (currently a placeholder)

Functions:

  • sal::process::which(command_name: &str) -> Option<String>: Checks if a command exists in the system's PATH. Returns the full path if found.

    if let Some(path) = sal::process::which("git") {
        println!("Git found at: {}", path);
    }
    
  • sal::process::kill(pattern: &str) -> Result<String, ProcessError>: Kills processes matching the given pattern (name or part of the command line). Uses taskkill on Windows and pkill -f on Unix-like systems.

    match sal::process::kill("my-server-proc") {
        Ok(msg) => println!("{}", msg), // "Successfully killed processes" or "No matching processes found"
        Err(e) => eprintln!("Error killing process: {}", e),
    }
    
  • sal::process::process_list(pattern: &str) -> Result<Vec<ProcessInfo>, ProcessError>: Lists running processes, optionally filtering by a pattern (substring match on name). If pattern is empty, lists all accessible processes. Uses wmic on Windows and ps on Unix-like systems.

    match sal::process::process_list("nginx") {
        Ok(procs) => {
            for p in procs {
                println!("PID: {}, Name: {}", p.pid, p.name);
            }
        },
        Err(e) => eprintln!("Error listing processes: {}", e),
    }
    
  • sal::process::process_get(pattern: &str) -> Result<ProcessInfo, ProcessError>: Retrieves a single ProcessInfo for a process matching pattern. Returns an error if zero or multiple processes match.

    match sal::process::process_get("unique_process_name") {
        Ok(p) => println!("Found: PID {}, Name {}", p.pid, p.name),
        Err(sal::process::ProcessError::NoProcessFound(patt)) => eprintln!("No process like '{}'", patt),
        Err(sal::process::ProcessError::MultipleProcessesFound(patt, count)) => {
            eprintln!("Found {} processes like '{}'", count, patt);
        }
        Err(e) => eprintln!("Error: {}", e),
    }
    

Error Handling:

  • ProcessError: Enum for errors specific to process management (e.g., CommandExecutionFailed, NoProcessFound, MultipleProcessesFound).

Examples

Running a simple command

use sal::process;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let result = process::run("echo 'Hello from SAL!'").execute()?;
    println!("Output: {}", result.stdout);
    Ok(())
}

Running a multi-line script silently

use sal::process;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let script = r#"
        echo "Starting script..."
        date
        echo "Script finished."
    "#;
    let result = process::run(script).silent(true).execute()?;
    if result.success {
        println!("Script executed successfully. Output:\n{}", result.stdout);
    } else {
        eprintln!("Script failed. Error:\n{}", result.stderr);
    }
    Ok(())
}

Checking if a command exists and then running it

use sal::process;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    if process::which("figlet").is_some() {
        process::run("figlet 'SAL Process'").execute()?;
    } else {
        println!("Figlet not found, using echo instead:");
        process::run("echo 'SAL Process'").execute()?;
    }
    Ok(())
}