turtle-build 0.4.10

Ninja-compatible build system for high-level programming languages
use async_trait::async_trait;
use std::{error::Error, process::Output};
use tokio::{process::Command, sync::Semaphore};

#[async_trait]
pub trait CommandRunner {
    async fn run(&self, command: &str) -> Result<Output, Box<dyn Error>>;
}

#[derive(Debug)]
pub struct OsCommandRunner {
    semaphore: Semaphore,
}

impl OsCommandRunner {
    pub fn new(job_limit: usize) -> Self {
        Self {
            semaphore: Semaphore::new(job_limit),
        }
    }
}

#[async_trait]
impl CommandRunner for OsCommandRunner {
    async fn run(&self, command: &str) -> Result<Output, Box<dyn Error>> {
        let permit = self.semaphore.acquire().await?;

        let output = if cfg!(target_os = "windows") {
            let components = command.split_whitespace().collect::<Vec<_>>();
            Command::new(components[0])
                .args(&components[1..])
                .output()
                .await?
        } else {
            Command::new("sh").arg("-ec").arg(command).output().await?
        };

        drop(permit);

        Ok(output)
    }
}