robinpath_modules/modules/
shell_mod.rs1use 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 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}