webgpu-shim 0.1.0

Webgpu shim based on wgpu
use downloader::{Download, Downloader};
use std::env;
use std::fs;
use std::path::PathBuf;

const WEBGPU_H_COMMIT_ID: &str = "673658bc2bd70ec39fc55ebe6bb0173cf6d0a603";
const WEBGPU_H_SHA256_SUM: &str =
    "a483031c3fed05ea5dd1c74082a71676c46c5b2b820ccca10da515c033efc997";

fn main() {
    println!("cargo:rerun-if-changed=wgpu.h");
    println!("cargo:rerun-if-changed=dep/webgpu-headers/webgpu.h");

    let webgpu_h_download_dir = download_webgpu_header();

    #[rustfmt::skip]
    let types_to_rename = vec![
        ("WGPUAdapter", "WGPUAdapterImpl"),
        ("WGPUBindGroup", "WGPUBindGroupImpl"),
        ("WGPUBindGroupLayout", "WGPUBindGroupLayoutImpl"),
        ("WGPUBuffer", "WGPUBufferImpl"),
        ("WGPUCommandBuffer", "WGPUCommandBufferImpl"),
        ("WGPUCommandEncoder", "WGPUCommandEncoderImpl"),
        ("WGPUComputePassEncoder", "WGPUComputePassEncoderImpl"),
        ("WGPUComputePipeline", "WGPUComputePipelineImpl"),
        ("WGPUDevice", "WGPUDeviceImpl"),
        ("WGPUInstance", "WGPUInstanceImpl"),
        ("WGPUPipelineLayout", "WGPUPipelineLayoutImpl"),
        ("WGPUQuerySet", "WGPUQuerySetImpl"),
        ("WGPUQueue", "WGPUQueueImpl"),
        ("WGPURenderBundle", "WGPURenderBundleImpl"),
        ("WGPURenderBundleEncoder", "WGPURenderBundleEncoderImpl"),
        ("WGPURenderPassEncoder", "WGPURenderPassEncoderImpl"),
        ("WGPURenderPipeline", "WGPURenderPipelineImpl"),
        ("WGPUSampler", "WGPUSamplerImpl"),
        ("WGPUShaderModule", "WGPUShaderModuleImpl"),
        ("WGPUSurface", "WGPUSurfaceImpl"),
        ("WGPUTexture", "WGPUTextureImpl"),
        ("WGPUTextureView", "WGPUTextureViewImpl"),
    ];

    let mut builder = bindgen::Builder::default()
        .header("wgpu.h")
        .clang_arg(format!("-I{}", webgpu_h_download_dir))
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        .allowlist_item("WGPU.*")
        .allowlist_item("wgpu.*")
        .prepend_enum_name(false)
        .size_t_is_usize(true)
        .ignore_functions()
        .layout_tests(true)
        .clang_macro_fallback();

    for (old_name, new_name) in types_to_rename {
        let line = format!("pub type {old_name} = *const crate::{new_name};");
        builder = builder
            .blocklist_type(old_name)
            .blocklist_type(format!("{old_name}Impl"))
            .raw_line(line);
    }

    let bindings = builder.generate().expect("Unable to generate bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings.write_to_file(out_path.join("bindings.rs")).expect("Couldn't write bindings!");
}

fn download_webgpu_header() -> String {
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());

    let file = out_path.join("webgpu.h");
    if !sha256::try_digest(&file).is_ok_and(|sum| sum == WEBGPU_H_SHA256_SUM) {
        fs::create_dir_all(out_path.clone()).expect("Failed to create output directory");
        let mut downloader = Downloader::builder()
            .download_folder(&out_path)
            .build()
            .expect("Unable to build download builder");
        let downloads = downloader
            .download(&[Download::new(&format!(
                "https://github.com/webgpu-native/webgpu-headers/raw/{WEBGPU_H_COMMIT_ID}/webgpu.h"
            ))])
            .expect("Failed to download maplibre-native static lib")
            .into_iter();
        for download in downloads {
            if let Err(err) = download {
                panic!("Unexpected error from downloader: {err}");
            }
        }
        if let Err(e) = sha256::try_digest(file) {
            panic!("Unable to validate webgpu.h: {e}");
        }
    }

    let out_path = out_path.as_os_str().to_str().expect("Failed to resolve webgpu include dir");
    println!("cargo:rustc-env=WEBGPU_SHIM_WEBGPU_HEADER_INCLUDE_DIR={}", out_path);
    out_path.to_owned()
}