fyrer 0.2.2

A lightweight tool to run multiple dev servers concurrently
use crate::config::Service;
use crate::env_parser::parse_env;
use colored::Colorize;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::Stdio;
use tokio::io::AsyncBufReadExt;
use tokio::process::{Child, Command};

pub async fn spawn_service(
    service: &Service,
    color: colored::Color,
    wait: bool,
    max_name_len: usize,
) -> Option<Child> {
    #[cfg(unix)]
    let mut cmd = Command::new("sh");
    #[cfg(unix)]
    cmd.arg("-c").arg(&service.cmd);

    #[cfg(windows)]
    let mut cmd = Command::new("cmd");
    #[cfg(windows)]
    cmd.arg("/C").arg(&service.cmd);

    cmd.current_dir(&service.dir);
    cmd.stdout(Stdio::piped());
    cmd.stderr(Stdio::piped());
    let dotenv_path;
    if let Some(env_path) = &service.env_path {
        dotenv_path = Path::new(&service.dir).join(env_path);
    } else {
        dotenv_path = Path::new(&service.dir).join(".env");
    }

    if let Ok(mut dotenv) = File::open(&dotenv_path) {
        let mut content = String::new();
        if let Err(_) = dotenv.read_to_string(&mut content) {
        } else {
            let envs = parse_env(&content);
            for (k, v) in envs.into_iter() {
                cmd.env(k, v);
            }
        }
    }

    if let Some(envs) = &service.env {
        for (key, value) in envs {
            cmd.env(key, value);
        }
    }

    let out_prefix = format!("[{}]", service.name);
    let padded_name = format!("{:<width$}", out_prefix.clone(), width = max_name_len);

    println!(
        "├─{} ➤ Starting service... {}",
        padded_name.color(color).bold(),
        service.cmd.bright_white()
    );

    let mut child = cmd.spawn().ok()?;
    let stdout = child.stdout.take().expect("Failed to capture stdout");
    let stderr = child.stderr.take().expect("Failed to capture stderr");

    let quiet = service.quiet.unwrap_or(false);

    if !quiet {
        let name = service.name.clone();
        let mut stdout_reader = tokio::io::BufReader::new(stdout).lines();
        let mut stderr_reader = tokio::io::BufReader::new(stderr).lines();

        let name_prefix = format!("[{}] ", name).color(color).bold();
        let out_prefix = name_prefix.clone();
        let err_prefix = name_prefix.clone();

        tokio::spawn(async move {
            while let Ok(Some(line)) = stdout_reader.next_line().await {
                let padded_name = format!("{:<width$}", out_prefix.clone(), width = max_name_len);
                println!(
                    "├─{}{}",
                    padded_name.bright_cyan().bold(),
                    line.color(color)
                );
            }
        });

        tokio::spawn(async move {
            while let Ok(Some(line)) = stderr_reader.next_line().await {
                let padded_name = format!("{:<width$}", err_prefix.clone(), width = max_name_len);
                eprintln!(
                    "├─{}{}",
                    padded_name.bright_red().bold(),
                    line.bright_red().bold()
                );
            }
        });
    }

    if wait {
        let _ = child.wait().await;
        return None;
    }

    Some(child)
}