Skip to content

Instantly share code, notes, and snippets.

@sheepla
Created December 30, 2024 09:19
Show Gist options
  • Save sheepla/0bc5e37ece2ecb2dcad095fa09c64f71 to your computer and use it in GitHub Desktop.
Save sheepla/0bc5e37ece2ecb2dcad095fa09c64f71 to your computer and use it in GitHub Desktop.
Run command asynchronously in Rust
use eyre::{Result, WrapErr};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufWriter};
use tokio::process::Command; // Use eyre for error handling
pub async fn run_command<W: tokio::io::AsyncWrite + Send + 'static + Unpin>(
cmd: &str,
args: &[&str],
stdout_writer: W,
stderr_writer: W,
) -> Result<()> {
// Create a buffered writer for stdout and stderr
let mut stdout_writer = BufWriter::new(stdout_writer);
let mut stderr_writer = BufWriter::new(stderr_writer);
// Spawn the command
let mut child = Command::new(cmd)
.args(args)
.stdout(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()
.wrap_err_with(|| format!("Failed to start command: {}", cmd))?; // Improved error handling
// Capture the stdout and stderr
let stdout = child
.stdout
.take()
.ok_or_else(|| eyre::eyre!("Failed to capture stdout"))?;
let stderr = child
.stderr
.take()
.ok_or_else(|| eyre::eyre!("Failed to capture stderr"))?;
// Spawn tasks to handle stdout and stderr
let stdout_task = tokio::spawn(async move {
let mut reader = tokio::io::BufReader::new(stdout);
let mut buffer = Vec::new();
while let Ok(bytes_read) = reader.read_until(b'\n', &mut buffer).await {
if bytes_read == 0 {
break; // EOF
}
stdout_writer.write_all(&buffer).await.unwrap();
buffer.clear();
}
});
let stderr_task = tokio::spawn(async move {
let mut reader = tokio::io::BufReader::new(stderr);
let mut buffer = Vec::new();
while let Ok(bytes_read) = reader.read_until(b'\n', &mut buffer).await {
if bytes_read == 0 {
break; // EOF
}
stderr_writer.write_all(&buffer).await.unwrap();
buffer.clear();
}
});
let status = child.wait().await.wrap_err("Failed to wait for command")?;
stdout_task.await.wrap_err("Failed to await stdout task")?;
stderr_task.await.wrap_err("Failed to await stderr task")?;
if !status.success() {
return Err(eyre::eyre!(
"Command exited with a non-zero status: {}",
status
));
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment