Skip to main content

robinpath_modules/modules/
shell_mod.rs

1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4    rp.register_builtin("shell.exec", |args, _| {
5        let cmd = args.first().map(|v| v.to_display_string()).unwrap_or_default();
6        run_shell_command(&cmd)
7    });
8
9    rp.register_builtin("shell.run", |args, _| {
10        let cmd = args.first().map(|v| v.to_display_string()).unwrap_or_default();
11        let result = run_shell_command(&cmd)?;
12        if let Value::Object(obj) = &result {
13            Ok(obj
14                .get("stdout")
15                .cloned()
16                .unwrap_or(Value::String(String::new())))
17        } else {
18            Ok(Value::String(String::new()))
19        }
20    });
21
22    rp.register_builtin("shell.which", |args, _| {
23        let cmd = args.first().map(|v| v.to_display_string()).unwrap_or_default();
24        let which_cmd = if cfg!(target_os = "windows") {
25            format!("where {}", cmd)
26        } else {
27            format!("which {}", cmd)
28        };
29        match run_shell_raw(&which_cmd) {
30            Ok(output) => {
31                let path = output.trim().to_string();
32                if path.is_empty() {
33                    Ok(Value::Null)
34                } else {
35                    // Return just the first line (first match)
36                    Ok(Value::String(
37                        path.lines().next().unwrap_or("").to_string(),
38                    ))
39                }
40            }
41            Err(_) => Ok(Value::Null),
42        }
43    });
44
45    rp.register_builtin("shell.env", |_args, _| {
46        let mut obj = indexmap::IndexMap::new();
47        for (k, v) in std::env::vars() {
48            obj.insert(k, Value::String(v));
49        }
50        Ok(Value::Object(obj))
51    });
52
53    rp.register_builtin("shell.cwd", |_args, _| {
54        match std::env::current_dir() {
55            Ok(p) => Ok(Value::String(p.to_string_lossy().to_string())),
56            Err(e) => Err(format!("shell.cwd error: {}", e)),
57        }
58    });
59
60    rp.register_builtin("shell.pid", |_args, _| {
61        Ok(Value::Number(std::process::id() as f64))
62    });
63
64    rp.register_builtin("shell.platform", |_args, _| {
65        Ok(Value::String(std::env::consts::OS.to_string()))
66    });
67}
68
69fn run_shell_command(cmd: &str) -> Result<Value, String> {
70    let output = if cfg!(target_os = "windows") {
71        std::process::Command::new("cmd")
72            .args(["/C", cmd])
73            .output()
74    } else {
75        std::process::Command::new("sh")
76            .args(["-c", cmd])
77            .output()
78    };
79
80    match output {
81        Ok(out) => {
82            let mut obj = indexmap::IndexMap::new();
83            obj.insert(
84                "stdout".to_string(),
85                Value::String(String::from_utf8_lossy(&out.stdout).trim().to_string()),
86            );
87            obj.insert(
88                "stderr".to_string(),
89                Value::String(String::from_utf8_lossy(&out.stderr).trim().to_string()),
90            );
91            obj.insert(
92                "exitCode".to_string(),
93                Value::Number(out.status.code().unwrap_or(-1) as f64),
94            );
95            Ok(Value::Object(obj))
96        }
97        Err(e) => Err(format!("shell.exec error: {}", e)),
98    }
99}
100
101fn run_shell_raw(cmd: &str) -> Result<String, String> {
102    let output = if cfg!(target_os = "windows") {
103        std::process::Command::new("cmd")
104            .args(["/C", cmd])
105            .output()
106    } else {
107        std::process::Command::new("sh")
108            .args(["-c", cmd])
109            .output()
110    };
111
112    match output {
113        Ok(out) => Ok(String::from_utf8_lossy(&out.stdout).to_string()),
114        Err(e) => Err(format!("shell error: {}", e)),
115    }
116}