libnftables1-sys 0.1.2

FFI bindings for libnftables1.
extern crate bindgen;

use std::env;
use std::path::PathBuf;

fn get_env<T: From<String>>(var: impl AsRef<str>) -> Option<T> {
    println!("cargo:rerun-if-env-changed={}", var.as_ref());
    env::var(var.as_ref()).map(Into::into).ok()
}

fn find_library(libname: &str, env_prefix: &str, statik: bool) {
    if let Some(lib_dir) = get_env::<PathBuf>(format!("{env_prefix}_LIB_DIR")) {
        if !lib_dir.is_dir() {
            panic!(
                "{libname} library directory does not exist: {}",
                lib_dir.display()
            );
        }
        println!("cargo:rustc-link-search=native={}", lib_dir.display());
        println!("cargo:rustc-link-lib={libname}");
    } else {
        pkg_config::Config::new()
            .statik(statik)
            .probe(libname)
            .expect("package not found");
    }
}

fn build_includes(deps: &[(&str, &str)]) -> Vec<String> {
    deps.iter()
        .filter_map(|(_libname, env_prefix)| {
            get_env::<String>(format!("{env_prefix}_INCLUDE_DIR")).map(|dir| format!("-I{dir}"))
        })
        .collect()
}

const LIBNFTABLES_WITH_SUBDEPS: &[(&str, &str)] = &[
    ("gmp", "GMP"),
    ("jansson", "JANSSON"),
    ("libmnl", "LIBMNL"),
    ("libnftnl", "LIBNFTNL"),
    ("xtables", "XTABLES"),
    ("libnftables", "NFTABLES"),
];

const LIBNFTABLES_DEPS: &[(&str, &str)] = &[("libnftables", "NFTABLES")];

fn main() {
    // Find all the libraries we need. Because `libnftables`'s `.pc` file does
    // not list its dependencies, we need to list them here explicitly when
    // building statically.
    let (deps, statik) = match env::var("CARGO_CFG_TARGET_ENV")
        .unwrap_or_default()
        .as_ref()
    {
        "musl" => (LIBNFTABLES_WITH_SUBDEPS, true),
        _ => (LIBNFTABLES_DEPS, false),
    };
    for (libname, env_prefix) in deps {
        find_library(libname, env_prefix, statik);
    }

    // The bindgen::Builder is the main entry point
    // to bindgen, and lets you build up options for
    // the resulting bindings.
    let bindings = bindgen::Builder::default()
        .clang_args(build_includes(deps))
        // generate only nftables
        .allowlist_function("nft_.*")
        .allowlist_type("nft_.*")
        // do not bind functions using FILE
        .opaque_type("nft_ctx_set_output")
        .opaque_type("nft_ctx_set_error")
        // remove one extra type
        .blocklist_type("__uint32_t")
        // simplify constants names
        .prepend_enum_name(false)
        // The input header we would like to generate
        // bindings for.
        .header("src/wrapper.h")
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

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