Skip to main content

turtle_build/infrastructure/command_runner/
os.rs

1use crate::infrastructure::{CommandError, CommandRunner};
2use async_trait::async_trait;
3use std::process::{ExitStatus, Output, Stdio};
4use tokio::{process::Command, sync::Semaphore};
5
6/// A command runner backed by an operating system.
7#[derive(Debug)]
8pub struct OsCommandRunner {
9    semaphore: Semaphore,
10}
11
12impl OsCommandRunner {
13    /// Creates a command runner.
14    pub fn new(job_limit: usize) -> Self {
15        Self {
16            semaphore: Semaphore::new(job_limit),
17        }
18    }
19
20    fn create_command(command: &str) -> Command {
21        if cfg!(target_os = "windows") {
22            let components = command.split_whitespace().collect::<Vec<_>>();
23            let mut process = Command::new(components[0]);
24
25            process.args(&components[1..]);
26            process
27        } else {
28            let mut process = Command::new("sh");
29
30            process.arg("-ec").arg(command);
31            process
32        }
33    }
34}
35
36#[async_trait]
37impl CommandRunner for OsCommandRunner {
38    async fn run(&self, command: &str) -> Result<Output, CommandError> {
39        let _permit = self.semaphore.acquire().await?;
40
41        Ok(Self::create_command(command)
42            .stdin(Stdio::null())
43            .output()
44            .await?)
45    }
46
47    async fn run_with_console(&self, command: &str) -> Result<ExitStatus, CommandError> {
48        let _permit = self.semaphore.acquire().await?;
49
50        // Inherit standard input, output, and error.
51        Ok(Self::create_command(command).status().await?)
52    }
53}