tatara_lisp_script/stdlib/
process.rs1use std::process::{Command, Stdio};
16use std::sync::Arc;
17
18use tatara_lisp_eval::{Arity, EvalError, Interpreter, Value};
19
20use crate::script_ctx::ScriptCtx;
21use crate::stdlib::env::str_arg;
22
23pub fn install(interp: &mut Interpreter<ScriptCtx>) {
24 interp.register_fn(
25 "exec-check",
26 Arity::AtLeast(1),
27 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
28 let (cmd, rest) = split_cmd(args, "exec-check", sp)?;
29 let status = Command::new(&*cmd)
30 .args(rest.iter().map(|s| s.as_ref()))
31 .stdin(Stdio::inherit())
32 .stdout(Stdio::inherit())
33 .stderr(Stdio::inherit())
34 .status()
35 .map_err(|e| EvalError::native_fn("exec-check", e.to_string(), sp))?;
36 Ok(Value::Int(status.code().unwrap_or(-1) as i64))
37 },
38 );
39
40 interp.register_fn(
41 "exec-ok?",
42 Arity::AtLeast(1),
43 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
44 let (cmd, rest) = split_cmd(args, "exec-ok?", sp)?;
45 let status = Command::new(&*cmd)
46 .args(rest.iter().map(|s| s.as_ref()))
47 .stdin(Stdio::null())
48 .stdout(Stdio::null())
49 .stderr(Stdio::null())
50 .status()
51 .map_err(|e| EvalError::native_fn("exec-ok?", e.to_string(), sp))?;
52 Ok(Value::Bool(status.success()))
53 },
54 );
55
56 interp.register_fn(
57 "exec-capture",
58 Arity::AtLeast(1),
59 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
60 let (cmd, rest) = split_cmd(args, "exec-capture", sp)?;
61 let out = Command::new(&*cmd)
62 .args(rest.iter().map(|s| s.as_ref()))
63 .stdin(Stdio::null())
64 .output()
65 .map_err(|e| EvalError::native_fn("exec-capture", e.to_string(), sp))?;
66 Ok(capture_result(&out))
67 },
68 );
69
70 interp.register_fn(
71 "sh-exec",
72 Arity::Exact(1),
73 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
74 let script = str_arg(&args[0], "sh-exec", sp)?;
75 let out = Command::new("sh")
76 .arg("-c")
77 .arg(&*script)
78 .stdin(Stdio::null())
79 .output()
80 .map_err(|e| EvalError::native_fn("sh-exec", e.to_string(), sp))?;
81 Ok(capture_result(&out))
82 },
83 );
84}
85
86fn split_cmd(
87 args: &[Value],
88 fname: &'static str,
89 sp: tatara_lisp::Span,
90) -> Result<(Arc<str>, Vec<Arc<str>>), EvalError> {
91 let mut it = args.iter();
92 let cmd = str_arg(
93 it.next().ok_or_else(|| {
94 EvalError::native_fn(fname, "expected at least 1 argument".to_string(), sp)
95 })?,
96 fname,
97 sp,
98 )?;
99 let rest = it
100 .map(|v| str_arg(v, fname, sp))
101 .collect::<Result<Vec<_>, _>>()?;
102 Ok((cmd, rest))
103}
104
105fn capture_result(out: &std::process::Output) -> Value {
106 Value::list(vec![
107 Value::list(vec![
108 Value::Keyword(Arc::from("status")),
109 Value::Int(out.status.code().unwrap_or(-1) as i64),
110 ]),
111 Value::list(vec![
112 Value::Keyword(Arc::from("stdout")),
113 Value::Str(Arc::from(String::from_utf8_lossy(&out.stdout).as_ref())),
114 ]),
115 Value::list(vec![
116 Value::Keyword(Arc::from("stderr")),
117 Value::Str(Arc::from(String::from_utf8_lossy(&out.stderr).as_ref())),
118 ]),
119 ])
120}