Created
December 30, 2024 09:19
-
-
Save sheepla/0bc5e37ece2ecb2dcad095fa09c64f71 to your computer and use it in GitHub Desktop.
Run command asynchronously in Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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