extern crate bindgen;

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

fn main() {
    // Tell cargo to tell rustc to link the system pam
    // shared library.
    println!("cargo:rustc-link-lib=pam");

    // pam_misc is only supported on Linux afaik
    if cfg!(target_os = "linux") {
        println!("cargo:rustc-link-lib=pam_misc");
    }

    // Tell cargo to invalidate the built crate whenever the wrapper changes
    println!("cargo:rerun-if-changed=wrapper.h");

    // Prepare bindgen builder
    let mut builder = bindgen::Builder::default()
        // Our header
        .header("wrapper.h")
        // Use libc for c-types
        .ctypes_prefix("libc")
        // pam_handle_t is opaque
        .opaque_type("pam_handle_t")
        // Block varargs functions and related types for now
        // TODO: find a nice solution for this
        .blocklist_type("va_list")
        .blocklist_type("__va_list")
        .blocklist_type("__builtin_va_list")
        .blocklist_type("__va_list_tag")
        .blocklist_function("pam_v.*")
        .blocklist_function("pam_syslog")
        .blocklist_function("pam_prompt")
        // Allow all PAM constants
        .allowlist_var("PAM_.*")
        // Allow all PAM functions..
        .allowlist_function("pam_.*")
        // ..except module related functions (pam_sm_*)
        .blocklist_function("pam_sm_.*");

    // Platform-specific adaptions
    if cfg!(target_os = "linux") {
        builder = builder
            // Set macro constants to signed int, as all functions that accept
            // these constants use signed int as the parameter type
            .default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
            //
            // Use libc types so our signatures are slightly nicer
            .raw_line("use libc::{uid_t, gid_t, group, passwd, spwd};")
            .blocklist_type(".*gid_t")
            .blocklist_type(".*uid_t")
            .blocklist_type("group")
            .blocklist_type("passwd")
            .blocklist_type("spwd");
    } else if cfg!(target_os = "freebsd") || cfg!(target_os = "netbsd") {
        // XXX: this should include all OS that use openPAM
        builder = builder
            // Use libc types so our signatures are slightly nicer
            .raw_line("use libc::passwd;")
            .blocklist_type("passwd");
    }

    let bindings = builder
        // 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!");
}
