eccodes-sys 0.2.2

Low-level Rust bindings for latests ecCodes version.
Documentation
use std::{
    collections::HashSet,
    env,
    path::PathBuf,
    sync::{Arc, RwLock},
};

use bindgen::callbacks::{MacroParsingBehavior, ParseCallbacks};

// build script heavily inspired by proj-sys crate
// some parts of code from rust-bindgen

// currently the latest in apt on Github Actions
const MINIMUM_ECCODES_VERSION: &str = "2.16.0";
const PROBLEMATIC_MACROS: [&str; 5] = [
    "FP_NAN",
    "FP_INFINITE",
    "FP_ZERO",
    "FP_SUBNORMAL",
    "FP_NORMAL",
];

#[derive(Debug)]
struct MacroCallback {
    macros: Arc<RwLock<HashSet<String>>>,
}

impl ParseCallbacks for MacroCallback {
    fn will_parse_macro(&self, name: &str) -> MacroParsingBehavior {
        self.macros.write().unwrap().insert(name.into());

        if PROBLEMATIC_MACROS.contains(&name) {
            return MacroParsingBehavior::Ignore;
        }

        MacroParsingBehavior::Default
    }
}

fn main() {
    if cfg!(feature = "docs") {
        return;
    }

    let include_path;
    let lib_result = pkg_config::Config::new()
        .atleast_version(MINIMUM_ECCODES_VERSION)
        .probe("eccodes");

    match lib_result {
        Ok(pk) => {
            eprintln!(
                "Found installed ecCodes library to link at: {:?}",
                pk.link_paths[0]
            );
            println!("cargo:rustc-link-search={:?}", pk.link_paths[0]);
            println!("cargo:rustc-link-lib=eccodes");
            include_path = pk.include_paths[0].clone();
        }
        Err(err) => {
            panic!(
                "Cannot find existing ecCodes library. 
                Please check the README for information how to correctly install ecCodes.
                Additional error information: {}",
                err
            );
        }
    }

    //bindgen magic to avoid duplicate math.h type definitions
    let macros = Arc::new(RwLock::new(HashSet::new()));

    let tests = cfg!(feature = "tests");

    let bindings = bindgen::Builder::default()
        .clang_arg(format!("-I{}", include_path.to_string_lossy()))
        .trust_clang_mangling(false)
        .header("wrapper.h")
        .layout_tests(tests) //avoiding tests with UB
        .parse_callbacks(Box::new(MacroCallback {
            macros: macros.clone(),
        }))
        .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("Failed to write bindings to file");
}