use rune::Any;
use rune::ContextError;
use rune::Module;
use rune::alloc::Vec;
use rune::alloc::fmt::TryWrite;
use rune::runtime::Bytes;
use rune::runtime::Formatter;
use rune::runtime::Mut;
use rune::runtime::Shared;
use rune::runtime::Value;
use rune::runtime::VmResult;
use rune::vm_try;
use rune::vm_write;
use std::io;
use tokio::process;
use tracing::instrument;
#[rune::module(::process)]
pub fn module(_stdio: bool) -> Result<Module, ContextError> {
let mut module = Module::from_meta(module_meta)?;
module.ty::<Command>()?;
module.ty::<Child>()?;
module.ty::<ExitStatus>()?;
module.ty::<Output>()?;
module.ty::<Stdio>()?;
module.ty::<ChildStdin>()?;
module.ty::<ChildStdout>()?;
module.ty::<ChildStderr>()?;
module.function_meta(Command::string_debug)?;
module.function_meta(Command::new)?;
module.function_meta(Command::spawn)?;
module.function_meta(Command::arg)?;
module.function_meta(Command::args)?;
#[cfg(unix)]
module.function_meta(Command::arg0)?;
module.function_meta(Command::stdin)?;
module.function_meta(Command::stdout)?;
module.function_meta(Command::stderr)?;
module.function_meta(Child::string_debug)?;
module.function_meta(Child::stdin)?;
module.function_meta(Child::stdout)?;
module.function_meta(Child::stderr)?;
module.function_meta(Child::id)?;
module.function_meta(Child::start_kill)?;
module.function_meta(Child::kill)?;
module.function_meta(Child::wait)?;
module.function_meta(Child::wait_with_output)?;
module.function_meta(ExitStatus::string_debug)?;
module.function_meta(ExitStatus::string_display)?;
module.function_meta(ExitStatus::code)?;
module.function_meta(ExitStatus::success)?;
module.function_meta(Output::string_debug)?;
module.function_meta(Stdio::null)?;
module.function_meta(Stdio::inherit)?;
module.function_meta(Stdio::piped)?;
module.function_meta(ChildStdin::string_debug)?;
module.function_meta(ChildStdin::try_into_stdio)?;
module.function_meta(ChildStdout::string_debug)?;
module.function_meta(ChildStdout::try_into_stdio)?;
module.function_meta(ChildStderr::string_debug)?;
module.function_meta(ChildStderr::try_into_stdio)?;
Ok(module)
}
#[derive(Debug, Any)]
#[rune(item = ::process)]
struct Command {
inner: process::Command,
}
impl Command {
#[rune::function(vm_result, protocol = STRING_DEBUG)]
fn string_debug(&self, f: &mut Formatter) {
vm_write!(f, "{:?}", self);
}
#[rune::function(path = Self::new)]
fn new(command: &str) -> Self {
Self {
inner: process::Command::new(command),
}
}
#[rune::function(instance)]
fn args(&mut self, args: &[Value]) -> VmResult<()> {
for arg in args {
match arg {
Value::String(s) => {
self.inner.arg(&*vm_try!(s.borrow_ref()));
}
actual => {
return VmResult::expected::<String>(vm_try!(actual.type_info()));
}
}
}
VmResult::Ok(())
}
#[rune::function(instance)]
fn arg(&mut self, arg: &str) {
self.inner.arg(arg);
}
#[cfg(unix)]
#[rune::function(instance)]
fn arg0(&mut self, arg: &str) {
self.inner.arg0(arg);
}
#[rune::function(instance)]
fn stdin(&mut self, stdio: Stdio) {
self.inner.stdin(stdio.inner);
}
#[rune::function(instance)]
fn stdout(&mut self, stdio: Stdio) {
self.inner.stdout(stdio.inner);
}
#[rune::function(instance)]
fn stderr(&mut self, stdio: Stdio) {
self.inner.stderr(stdio.inner);
}
#[rune::function(instance)]
fn spawn(mut self) -> io::Result<Child> {
Ok(Child {
inner: Some(self.inner.spawn()?),
})
}
}
#[derive(Debug, Any)]
#[rune(item = ::process)]
struct Child {
inner: Option<process::Child>,
}
impl Child {
#[rune::function(vm_result, protocol = STRING_DEBUG)]
fn string_debug(&self, f: &mut Formatter) {
vm_write!(f, "{:?}", self);
}
#[rune::function(instance)]
fn stdin(&mut self) -> Option<ChildStdin> {
let Some(inner) = &mut self.inner else {
return None;
};
let stdin = inner.stdin.take()?;
Some(ChildStdin { inner: stdin })
}
#[rune::function(instance)]
fn stdout(&mut self) -> Option<ChildStdout> {
let Some(inner) = &mut self.inner else {
return None;
};
let stdout = inner.stdout.take()?;
Some(ChildStdout { inner: stdout })
}
#[rune::function(instance)]
fn stderr(&mut self) -> Option<ChildStderr> {
let Some(inner) = &mut self.inner else {
return None;
};
let stderr = inner.stderr.take()?;
Some(ChildStderr { inner: stderr })
}
#[rune::function(instance)]
fn id(&self) -> Option<u32> {
match &self.inner {
Some(inner) => inner.id(),
None => None,
}
}
#[rune::function(vm_result, instance)]
fn start_kill(&mut self) -> io::Result<()> {
let Some(inner) = &mut self.inner else {
rune::vm_panic!("already completed");
};
inner.start_kill()
}
#[rune::function(vm_result, instance, path = Self::kill)]
#[instrument(level = "info", skip_all)]
async fn kill(mut this: Mut<Self>) -> io::Result<()> {
let Some(inner) = &mut this.inner else {
rune::vm_panic!("already completed");
};
inner.kill().await
}
#[rune::function(vm_result, instance)]
#[instrument(level = "info", skip_all)]
async fn wait(self) -> io::Result<ExitStatus> {
let Some(mut inner) = self.inner else {
rune::vm_panic!("already completed");
};
let status = inner.wait().await?;
Ok(ExitStatus { status })
}
#[rune::function(vm_result, instance)]
#[instrument(level = "info", skip_all)]
async fn wait_with_output(self) -> io::Result<Output> {
let Some(inner) = self.inner else {
rune::vm_panic!("already completed");
};
let output = inner.wait_with_output().await?;
Ok(Output {
status: ExitStatus {
status: output.status,
},
stdout: Shared::new(Bytes::from_vec(Vec::try_from(output.stdout).vm?)).vm?,
stderr: Shared::new(Bytes::from_vec(Vec::try_from(output.stderr).vm?)).vm?,
})
}
}
#[derive(Debug, Any)]
#[rune(item = ::process)]
struct Output {
#[rune(get)]
status: ExitStatus,
#[rune(get)]
stdout: Shared<Bytes>,
#[rune(get)]
stderr: Shared<Bytes>,
}
impl Output {
#[rune::function(vm_result, protocol = STRING_DEBUG)]
fn string_debug(&self, f: &mut Formatter) {
vm_write!(f, "{:?}", self);
}
}
#[derive(Debug, Clone, Copy, Any)]
#[rune(item = ::process)]
struct ExitStatus {
status: std::process::ExitStatus,
}
impl ExitStatus {
#[rune::function(vm_result, protocol = STRING_DISPLAY)]
fn string_display(&self, f: &mut Formatter) {
vm_write!(f, "{}", self.status);
}
#[rune::function(vm_result, protocol = STRING_DEBUG)]
fn string_debug(&self, f: &mut Formatter) {
vm_write!(f, "{:?}", self);
}
#[rune::function]
fn success(&self) -> bool {
self.status.success()
}
#[rune::function]
fn code(&self) -> Option<i32> {
self.status.code()
}
}
#[derive(Debug, Any)]
#[rune(item = ::process)]
struct Stdio {
inner: std::process::Stdio,
}
impl Stdio {
#[rune::function(vm_result, protocol = STRING_DEBUG)]
fn string_debug(&self, f: &mut Formatter) {
vm_write!(f, "{:?}", self);
}
#[rune::function(path = Self::null)]
fn null() -> Self {
Self {
inner: std::process::Stdio::null(),
}
}
#[rune::function(path = Self::inherit)]
fn inherit() -> Self {
Self {
inner: std::process::Stdio::inherit(),
}
}
#[rune::function(path = Self::piped)]
fn piped() -> Self {
Self {
inner: std::process::Stdio::piped(),
}
}
}
macro_rules! stdio_stream {
($name:ident, $stream:tt) => {
#[derive(Debug, Any)]
#[rune(item = ::process)]
#[doc = concat!("The ", $stream, " stream for spawned children.")]
struct $name {
inner: process::$name,
}
impl $name {
#[rune::function(vm_result, protocol = STRING_DEBUG)]
fn string_debug(&self, f: &mut Formatter) {
vm_write!(f, "{:?}", self);
}
#[rune::function(instance)]
fn try_into_stdio(self) -> Result<Stdio, std::io::Error> {
Ok(Stdio {
inner: self.inner.try_into()?,
})
}
}
};
}
stdio_stream!(ChildStdin, "stdin");
stdio_stream!(ChildStdout, "stdout");
stdio_stream!(ChildStderr, "stderr");