1use crate::proto::b64_encode;
9use crate::store::expand;
10use serde_json::{json, Value};
11use std::io::Write;
12use std::process::{Command, Stdio};
13
14pub struct ExecResult {
16 pub code: Option<i32>,
18 pub stdout: Vec<u8>,
20 pub stderr: Vec<u8>,
22}
23
24pub fn run_raw(req: &Value) -> Result<ExecResult, String> {
28 let Some(program) = req["program"].as_str() else {
29 return Err("no_program".to_string());
30 };
31 let mut cmd = Command::new(program);
32 if let Some(args) = req["args"].as_array() {
33 for a in args {
34 if let Some(s) = a.as_str() {
35 cmd.arg(s);
36 }
37 }
38 }
39 if let Some(cwd) = req["cwd"].as_str() {
40 cmd.current_dir(expand(cwd));
41 }
42 if let Some(env) = req["env"].as_object() {
43 for (k, v) in env {
44 if let Some(s) = v.as_str() {
45 cmd.env(k, s);
46 }
47 }
48 }
49 cmd.stdin(Stdio::piped())
50 .stdout(Stdio::piped())
51 .stderr(Stdio::piped());
52
53 let mut child = cmd.spawn().map_err(|e| e.to_string())?;
54 if let Some(mut si) = child.stdin.take() {
56 if let Some(input) = req["stdin"].as_str() {
57 let _ = si.write_all(input.as_bytes());
58 }
59 }
60 let out = child.wait_with_output().map_err(|e| e.to_string())?;
61 Ok(ExecResult {
62 code: out.status.code(),
63 stdout: out.stdout,
64 stderr: out.stderr,
65 })
66}
67
68pub fn run(req: &Value) -> Value {
70 match run_raw(req) {
71 Ok(r) => json!({
72 "ok": true,
73 "code": r.code,
74 "stdout": b64_encode(&r.stdout),
75 "stderr": b64_encode(&r.stderr),
76 }),
77 Err(e) => json!({"ok": false, "err": e}),
78 }
79}