Skip to main content

sal_process/
rhai.rs

1//! Rhai wrappers for Process module functions
2//!
3//! This module provides Rhai wrappers for the functions in the Process module.
4
5use crate::{self as process, CommandResult, ProcessError, ProcessInfo, RunError};
6use rhai::{Array, Dynamic, Engine, EvalAltResult, Map};
7use std::clone::Clone;
8
9/// Register Process module functions with the Rhai engine
10///
11/// # Arguments
12///
13/// * `engine` - The Rhai engine to register the functions with
14///
15/// # Returns
16///
17/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
18pub fn register_process_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
19    // Register types
20    // register_process_types(engine)?; // Removed
21
22    // Register CommandResult type and its methods
23    engine.register_type_with_name::<CommandResult>("CommandResult");
24    engine.register_get("stdout", |r: &mut CommandResult| r.stdout.clone());
25    engine.register_get("stderr", |r: &mut CommandResult| r.stderr.clone());
26    engine.register_get("success", |r: &mut CommandResult| r.success);
27    engine.register_get("code", |r: &mut CommandResult| r.code);
28
29    // Register ProcessInfo type and its methods
30    engine.register_type_with_name::<ProcessInfo>("ProcessInfo");
31    engine.register_get("pid", |p: &mut ProcessInfo| p.pid);
32    engine.register_get("name", |p: &mut ProcessInfo| p.name.clone());
33    engine.register_get("memory", |p: &mut ProcessInfo| p.memory);
34    engine.register_get("cpu", |p: &mut ProcessInfo| p.cpu);
35
36    // Register CommandBuilder type and its methods
37    engine.register_type_with_name::<RhaiCommandBuilder>("CommandBuilder");
38    engine.register_fn("run", RhaiCommandBuilder::new_rhai); // This is the builder entry point
39    engine.register_fn("silent", RhaiCommandBuilder::silent); // Method on CommandBuilder
40    engine.register_fn("ignore_error", RhaiCommandBuilder::ignore_error); // Method on CommandBuilder
41    engine.register_fn("log", RhaiCommandBuilder::log); // Method on CommandBuilder
42    engine.register_fn("execute", RhaiCommandBuilder::execute_command); // Method on CommandBuilder
43
44    // Register other process management functions
45    engine.register_fn("which", which);
46    engine.register_fn("kill", kill);
47    engine.register_fn("process_list", process_list);
48    engine.register_fn("process_get", process_get);
49
50    // Register legacy functions for backward compatibility
51    engine.register_fn("run_command", run_command);
52    engine.register_fn("run_silent", run_silent);
53    engine.register_fn("run", run_with_options);
54
55    Ok(())
56}
57
58// Helper functions for error conversion
59fn run_error_to_rhai_error<T>(result: Result<T, RunError>) -> Result<T, Box<EvalAltResult>> {
60    result.map_err(|e| {
61        Box::new(EvalAltResult::ErrorRuntime(
62            format!("Run error: {}", e).into(),
63            rhai::Position::NONE,
64        ))
65    })
66}
67
68// Define a Rhai-facing builder struct
69#[derive(Clone)]
70struct RhaiCommandBuilder {
71    command: String,
72    die_on_error: bool,
73    is_silent: bool,
74    enable_log: bool,
75}
76
77impl RhaiCommandBuilder {
78    // Constructor function for Rhai (registered as `run`)
79    pub fn new_rhai(command: &str) -> Self {
80        Self {
81            command: command.to_string(),
82            die_on_error: true, // Default: die on error
83            is_silent: false,
84            enable_log: false,
85        }
86    }
87
88    // Rhai method: .silent()
89    pub fn silent(mut self) -> Self {
90        self.is_silent = true;
91        self
92    }
93
94    // Rhai method: .ignore_error()
95    pub fn ignore_error(mut self) -> Self {
96        self.die_on_error = false;
97        self
98    }
99
100    // Rhai method: .log()
101    pub fn log(mut self) -> Self {
102        self.enable_log = true;
103        self
104    }
105
106    // Rhai method: .execute() - Execute the command
107    pub fn execute_command(self) -> Result<CommandResult, Box<EvalAltResult>> {
108        let builder = process::run(&self.command)
109            .die(self.die_on_error)
110            .silent(self.is_silent)
111            .log(self.enable_log);
112
113        // Execute the command
114        run_error_to_rhai_error(builder.execute())
115    }
116}
117
118fn process_error_to_rhai_error<T>(
119    result: Result<T, ProcessError>,
120) -> Result<T, Box<EvalAltResult>> {
121    result.map_err(|e| {
122        Box::new(EvalAltResult::ErrorRuntime(
123            format!("Process error: {}", e).into(),
124            rhai::Position::NONE,
125        ))
126    })
127}
128
129//
130// Process Management Function Wrappers
131//
132
133/// Wrapper for process::which
134///
135/// Check if a command exists in PATH.
136pub fn which(cmd: &str) -> Dynamic {
137    match process::which(cmd) {
138        Some(path) => path.into(),
139        None => Dynamic::UNIT,
140    }
141}
142
143/// Wrapper for process::kill
144///
145/// Kill processes matching a pattern.
146pub fn kill(pattern: &str) -> Result<String, Box<EvalAltResult>> {
147    process_error_to_rhai_error(process::kill(pattern))
148}
149
150/// Wrapper for process::process_list
151///
152/// List processes matching a pattern (or all if pattern is empty).
153pub fn process_list(pattern: &str) -> Result<Array, Box<EvalAltResult>> {
154    let processes = process_error_to_rhai_error(process::process_list(pattern))?;
155
156    // Convert Vec<ProcessInfo> to Rhai Array
157    let mut array = Array::new();
158    for process in processes {
159        array.push(Dynamic::from(process));
160    }
161
162    Ok(array)
163}
164
165/// Wrapper for process::process_get
166///
167/// Get a single process matching the pattern (error if 0 or more than 1 match).
168pub fn process_get(pattern: &str) -> Result<ProcessInfo, Box<EvalAltResult>> {
169    process_error_to_rhai_error(process::process_get(pattern))
170}
171
172/// Legacy wrapper for process::run
173///
174/// Run a command and return the result.
175pub fn run_command(cmd: &str) -> Result<CommandResult, Box<EvalAltResult>> {
176    run_error_to_rhai_error(process::run(cmd).execute())
177}
178
179/// Legacy wrapper for process::run with silent option
180///
181/// Run a command silently and return the result.
182pub fn run_silent(cmd: &str) -> Result<CommandResult, Box<EvalAltResult>> {
183    run_error_to_rhai_error(process::run(cmd).silent(true).execute())
184}
185
186/// Legacy wrapper for process::run with options
187///
188/// Run a command with options and return the result.
189pub fn run_with_options(cmd: &str, options: Map) -> Result<CommandResult, Box<EvalAltResult>> {
190    let mut builder = process::run(cmd);
191
192    // Apply options
193    if let Some(silent) = options.get("silent") {
194        if let Ok(silent_bool) = silent.as_bool() {
195            builder = builder.silent(silent_bool);
196        }
197    }
198
199    if let Some(die) = options.get("die") {
200        if let Ok(die_bool) = die.as_bool() {
201            builder = builder.die(die_bool);
202        }
203    }
204
205    if let Some(log) = options.get("log") {
206        if let Ok(log_bool) = log.as_bool() {
207            builder = builder.log(log_bool);
208        }
209    }
210
211    run_error_to_rhai_error(builder.execute())
212}