tauri-plugin-bare-kit 0.3.0

Effortless JavaScript backends for Tauri apps - Android 🤖 iOS 📱Linux 🐧 Macos 🖥️ Windows 💩
use std::{
    env, fs,
    io::{self, Write},
    path::{Path, PathBuf},
};
use zip::ZipArchive;

const VERSION: &str = "2.4.3";

const COMMANDS: &[&str] = &[
    "bare_optimize_for_memory",
    "bare_new_worklet",
    "bare_start_file",
    "bare_start_utf8",
    "bare_start_bytes",
    "bare_read",
    "bare_write",
    "bare_update",
    "bare_suspend",
    "bare_resume",
    "bare_wakeup",
    "bare_terminate",
];

fn main() {
    let src = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let out = PathBuf::from(env::var("OUT_DIR").unwrap());
    let dest = out.join("bare-kit");
    let platform = env::var("CARGO_CFG_TARGET_OS").unwrap();

    fs::create_dir_all(&dest).unwrap();

    match &*platform {
        "macos" => {
            let prefix = "darwin/BareKit.xcframework/macos-arm64_x86_64/";

            extract_prebuilds(prefix, &dest);

            if cfg!(feature = "tests") {
                println!("cargo::rustc-link-arg=-Wl,-rpath,{}", dest.display());
                println!("cargo::rustc-link-search=framework={}", dest.display());
                println!("cargo::rustc-link-lib=framework=BareKit");
            }
        }
        "ios" => {
            let simulator = env::var("CARGO_CFG_TARGET_ENV").unwrap() == "sim";
            let prefix = if simulator {
                "ios/BareKit.xcframework/ios-arm64_x86_64-simulator/"
            } else {
                "ios/BareKit.xcframework/ios-arm64/"
            };

            extract_prebuilds(prefix, &dest);
        }
        "android" => {
            let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
            let arch = match &*arch {
                "arm" => "armeabi-v7a",
                "aarch64" => "arm64-v8a",
                "i686" => "x86",
                "x86_64" => "x86_64",
                arch => panic!("Unsupported architecture: {arch}"),
            };
            let prefix = format!("android/bare-kit/jni/{arch}/");

            extract_prebuilds(&*prefix, &dest);
        }
        "linux" => {
            let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
            let arch = match &*arch {
                "aarch64" => "arm64",
                "x86_64" => "x64",
                arch => panic!("Unsupported architecture: {arch}"),
            };
            let prefix = format!("linux/{arch}/");

            extract_prebuilds(&prefix, &dest);

            if cfg!(feature = "tests") {
                println!("cargo::rustc-link-arg=-Wl,-rpath={}", dest.display());
                println!("cargo::rustc-link-search=native={}", dest.display());
                println!("cargo::rustc-link-lib=bare-kit");
            }
        }
        "windows" => {
            let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
            let arch = match &*arch {
                "aarch64" => "arm64",
                "x86_64" => "x64",
                arch => panic!("Unsupported architecture: {arch}"),
            };
            let prefix = format!("win32/{arch}/");

            extract_prebuilds(&prefix, &dest);

            println!("cargo::rustc-link-search=native={}", dest.display());
            println!("cargo::rustc-link-lib=dylib=bare-kit");

            if cfg!(feature = "tests") {
                let profile = env::var("PROFILE").unwrap();

                fs::copy(
                    dest.join("bare-kit.dll"),
                    src.join(format!("target/{profile}/bare-kit.dll")),
                )
                .unwrap();
            }
        }
        platform => panic!("Unsupported platform: {platform}"),
    }

    println!("cargo::metadata=RESOURCE_DIR={}", dest.display());

    generate_bindings(&src, &out);

    tauri_plugin::Builder::new(COMMANDS).build();
}

fn extract_prebuilds<P: AsRef<Path>>(prefix: &str, dest: P) {
    let dest = dest.as_ref();
    let mut archive = get_prebuilds();

    for i in 0..archive.len() {
        let mut entry = archive.by_index(i).unwrap();

        let Some(name) = entry.enclosed_name() else {
            continue;
        };

        if !name.starts_with(&prefix) {
            continue;
        }

        let relative = &name.to_string_lossy()[prefix.len()..];

        if relative.is_empty() {
            continue;
        }

        let output = dest.join(relative);

        if entry.is_dir() {
            fs::create_dir_all(&output).unwrap();
        } else {
            if let Some(parent) = output.parent() {
                fs::create_dir_all(parent).unwrap();
            }

            io::copy(&mut entry, &mut fs::File::create(&output).unwrap()).unwrap();
        }
    }
}

fn get_prebuilds() -> ZipArchive<fs::File> {
    let uri = format!(
        "https://github.com/holepunchto/bare-kit/releases/download/v{VERSION}/prebuilds.zip"
    );
    let output = env::temp_dir().join(format!("tauri-plugin-bare-kit/{VERSION}.zip"));

    if fs::exists(&output).unwrap() {
        let result = ZipArchive::new(fs::File::open(&output).unwrap());

        if let Ok(archive) = result {
            return archive;
        }
    }

    fs::create_dir_all(&output.parent().unwrap()).unwrap();

    rustls::crypto::ring::default_provider()
        .install_default()
        .unwrap();

    let mut response = reqwest::blocking::get(uri)
        .unwrap()
        .error_for_status()
        .unwrap();
    let mut archive = fs::File::create(&output).unwrap();

    io::copy(&mut response, &mut archive).unwrap();
    archive.flush().unwrap();
    drop(archive);

    ZipArchive::new(fs::File::open(&output).unwrap()).unwrap()
}

fn generate_bindings<S: AsRef<Path>, O: AsRef<Path>>(src: &S, out: &O) {
    let src = src.as_ref();
    let out = out.as_ref();
    let header = src.join("include/bare-kit.h");

    let bindings = bindgen::Builder::default()
        .header(&*header.to_string_lossy())
        .allowlist_file(".*bare-kit\\.h")
        .allowlist_file(".*android\\.h")
        .allowlist_file(".*stdbool\\.h")
        .allowlist_file(".*stddef\\.h")
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        .generate()
        .unwrap();
    bindings.write_to_file(out.join("bare-kit.rs")).unwrap();
}