cloudini 0.3.0

The cloudini point cloud compression library for Rust.
fn main() {
    if std::env::var("CARGO_FEATURE_USE_CPP").is_ok() {
        build_cpp();
    }
}

fn build_cpp() {
    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
    let out_dir = std::env::var("OUT_DIR").unwrap();

    let cpp_src = format!("{}/cloudini-cpp/cloudini_lib", manifest_dir);
    let build_dir = format!("{}/cloudini_cpp_build", out_dir);

    println!("cargo:rerun-if-changed={}/src/cloudini.cpp", cpp_src);
    println!("cargo:rerun-if-changed={}/src/field_encoder.cpp", cpp_src);
    println!("cargo:rerun-if-changed={}/src/field_decoder.cpp", cpp_src);
    println!("cargo:rerun-if-changed={}/src/cloudini_c.cpp", cpp_src);
    println!(
        "cargo:rerun-if-changed={}/include/cloudini_lib/cloudini_c.h",
        cpp_src
    );

    let cmake_cache = std::path::PathBuf::from(&build_dir).join("CMakeCache.txt");
    if cmake_cache.exists() {
        let content = std::fs::read_to_string(&cmake_cache).unwrap_or_default();
        if !content.contains(&cpp_src) {
            std::fs::remove_dir_all(&build_dir).ok();
        }
    }

    std::fs::create_dir_all(&build_dir).expect("failed to create cmake build dir");

    let status = std::process::Command::new("cmake")
        .arg("-S")
        .arg(&cpp_src)
        .arg("-B")
        .arg(&build_dir)
        .arg("-DCMAKE_BUILD_TYPE=Release")
        .arg("-DCLOUDINI_BUILD_BENCHMARKS=OFF")
        .arg("-DCLOUDINI_BUILD_TOOLS=OFF")
        .arg("-DBUILD_TESTING=OFF")
        .arg("-DCLOUDINI_FORCE_VENDORED_DEPS=ON")
        .arg("-DBUILD_SHARED_LIBS=OFF")
        .status()
        .expect("cmake not found — install cmake to use the `use_cpp` feature");
    assert!(status.success(), "cmake configure step failed");

    let ncpus = std::thread::available_parallelism()
        .map(|n| n.get().to_string())
        .unwrap_or_else(|_| "4".to_string());

    let status = std::process::Command::new("cmake")
        .arg("--build")
        .arg(&build_dir)
        .arg("--config")
        .arg("Release")
        .arg("--parallel")
        .arg(&ncpus)
        .status()
        .expect("cmake --build failed");
    assert!(status.success(), "cmake build step failed");

    fn link_lib(build_dir: &str, lib_name: &str) {
        let pattern = format!("lib{}.a", lib_name);
        let output = std::process::Command::new("find")
            .arg(build_dir)
            .arg("-name")
            .arg(&pattern)
            .output()
            .expect("find command failed");

        let found = String::from_utf8(output.stdout).unwrap();
        for path_str in found.lines() {
            let path = std::path::Path::new(path_str.trim());
            if let Some(dir) = path.parent() {
                println!("cargo:rustc-link-search=native={}", dir.display());
            }
        }
        println!("cargo:rustc-link-lib=static={}", lib_name);
    }

    link_lib(&build_dir, "cloudini_lib");
    link_lib(&build_dir, "lz4_static");
    link_lib(&build_dir, "libzstd_static");

    let target = std::env::var("TARGET").unwrap_or_default();
    if target.contains("apple") {
        println!("cargo:rustc-link-lib=c++");
    } else {
        println!("cargo:rustc-link-lib=stdc++");
    }
}