tloop-arduino 0.0.4

Bundled arduino-cli wrapper for tloop — extracts and runs the embedded arduino-cli binary
// crates/loop-cli/build.rs
use std::path::Path;

fn main() {
    let out_dir  = std::env::var("OUT_DIR").unwrap();
    let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_else(|_| std::env::consts::OS.to_string());
    let ext = if target_os == "windows" { "exe" } else { "" };
    let bin_name = if ext.is_empty() { "arduino-cli".to_string() } else { format!("arduino-cli.{}", ext) };
    let bin_path = Path::new(&out_dir).join(&bin_name);

    if !bin_path.exists() {
        download_arduino_cli(&bin_path);
    }

    println!("cargo:rustc-env=ARDUINO_CLI_PATH={}", bin_path.display());
    println!("cargo:rerun-if-changed=build.rs");
}

fn download_arduino_cli(dest: &Path) {
    let version = "1.1.1";

    // Use CARGO_CFG_TARGET_* to detect the TARGET platform, not the build HOST.
    // cfg!(target_os = ...) in build.rs reflects the HOST, which is wrong for
    // cross-compilation (e.g. building for Linux from Windows).
    let target_os   = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_else(|_| std::env::consts::OS.to_string());
    let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_else(|_| std::env::consts::ARCH.to_string());

    let (os, archive_ext) = if target_os == "windows" {
        ("Windows", "zip")
    } else if target_os == "macos" {
        ("macOS", "tar.gz")
    } else {
        ("Linux", "tar.gz")
    };
    let arch = if target_arch == "aarch64" { "ARM64" } else { "64bit" };

    let url = format!(
        "https://github.com/arduino/arduino-cli/releases/download/v{version}/arduino-cli_{version}_{os}_{arch}.{archive_ext}"
    );

    let archive_path = format!("{}.{}", dest.display(), archive_ext);

    // Download
    let status = std::process::Command::new("curl")
        .args(["-L", "--fail", "-o", &archive_path, &url])
        .status()
        .expect("curl not found — install curl to build loop-arduino");
    assert!(status.success(), "failed to download arduino-cli from {url}");

    // Extract
    if archive_ext == "zip" {
        // On Windows, use PowerShell to extract
        let out_parent = dest.parent().unwrap().display().to_string();
        let status = std::process::Command::new("powershell")
            .args([
                "-NoProfile", "-Command",
                &format!("Expand-Archive -LiteralPath '{}' -DestinationPath '{}' -Force", archive_path, out_parent),
            ])
            .status()
            .expect("powershell not found");
        assert!(status.success(), "failed to extract arduino-cli zip");

        // arduino-cli extracts as arduino-cli.exe directly in the output dir
        let extracted = Path::new(&out_parent).join("arduino-cli.exe");
        if extracted.exists() && extracted != dest {
            std::fs::rename(&extracted, dest).unwrap();
        }
    } else {
        let out_parent = dest.parent().unwrap().display().to_string();
        let status = std::process::Command::new("tar")
            .args(["-xzf", &archive_path, "-C", &out_parent, "arduino-cli"])
            .status()
            .expect("tar not found");
        assert!(status.success(), "failed to extract arduino-cli tarball");
    }
}