turtle_build/infrastructure/
command_runner.rs1#[cfg(test)]
2mod fake;
3
4#[cfg(test)]
5pub use self::fake::FakeCommandRunner;
6use async_trait::async_trait;
7use core::error::Error;
8use std::process::Output;
9use tokio::{process::Command, sync::Semaphore};
10
11#[async_trait]
12pub trait CommandRunner {
13 async fn run(&self, command: &str) -> Result<Output, Box<dyn Error>>;
14}
15
16#[derive(Debug)]
18pub struct OsCommandRunner {
19 semaphore: Semaphore,
20}
21
22impl OsCommandRunner {
23 pub fn new(job_limit: usize) -> Self {
25 Self {
26 semaphore: Semaphore::new(job_limit),
27 }
28 }
29}
30
31#[async_trait]
32impl CommandRunner for OsCommandRunner {
33 async fn run(&self, command: &str) -> Result<Output, Box<dyn Error>> {
34 let permit = self.semaphore.acquire().await?;
35
36 let output = if cfg!(target_os = "windows") {
37 let components = command.split_whitespace().collect::<Vec<_>>();
38 Command::new(components[0])
39 .args(&components[1..])
40 .output()
41 .await?
42 } else {
43 Command::new("sh").arg("-ec").arg(command).output().await?
44 };
45
46 drop(permit);
47
48 Ok(output)
49 }
50}