eric-bindings 0.4.1

Rust bindings for the ELSTER Rich Client (ERiC)
Documentation
use std::{
    env, fmt, io,
    path::{Path, PathBuf},
};

#[derive(Debug)]
pub enum EricVersion {
    Eric38_1_6_0,
    Eric39_6_4_0,
    Eric40_1_8_0,
    Eric40_2_10_0,
}

impl fmt::Display for EricVersion {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let request_method = match self {
            Self::Eric38_1_6_0 => "38.1.6.0",
            Self::Eric39_6_4_0 => "39.6.4.0",
            Self::Eric40_1_8_0 => "40.1.8.0",
            Self::Eric40_2_10_0 => "40.2.10.0",
        };

        write!(f, "{}", request_method)
    }
}

pub fn main() -> io::Result<()> {
    #[cfg(feature = "generate-bindings")]
    generate_bindings()?;

    #[cfg(not(feature = "generate-bindings"))]
    {
        #[cfg(not(feature = "docs-rs"))]
        select_bindings()?;

        #[cfg(feature = "docs-rs")]
        select_bindings_for_docs_rs()?;
    }

    Ok(())
}

/// Select existing bindings
#[cfg(not(feature = "generate-bindings"))]
#[cfg(not(feature = "docs-rs"))]
fn select_bindings() -> io::Result<()> {
    let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").expect("Set by cargo");
    let is_windows = std::env::var("CARGO_CFG_WINDOWS").is_ok();
    let library_name =
        env::var("LIBRARY_NAME").expect("Missing environment variable 'LIBRARY_NAME'");
    let library_path =
        env::var("LIBRARY_PATH").expect("Missing environment variable 'LIBRARY_PATH'");
    let header_file = env::var("HEADER_FILE").expect("Missing environment variable 'HEADER_FILE'");
    let out_dir = env::var("OUT_DIR").expect("Can't read environment variable 'OUT_DIR'");
    let bindings_target = PathBuf::from(out_dir).join("bindings.rs");

    #[cfg(not(feature = "no-linking"))]
    {
        println!("cargo:rustc-link-search={}", library_path);
        println!("cargo:rustc-link-lib={}", library_name);
        println!("cargo:rerun-if-changed={}", header_file);
        println!("cargo:rustc-env=LD_LIBRARY_PATH={}", library_path);
    }

    let eric_version = if library_path.contains("38.1.6.0") {
        EricVersion::Eric38_1_6_0
    } else if library_path.contains("39.6.4.0") {
        EricVersion::Eric39_6_4_0
    } else if library_path.contains("40.1.8.0") {
        EricVersion::Eric40_1_8_0
    } else if library_path.contains("40.2.10.0") {
        EricVersion::Eric40_2_10_0
    } else {
        panic!("Missing bindings: Unknown Eric version");
    };
    let bindings_file = match (&eric_version, target_arch.as_ref(), is_windows) {
        (EricVersion::Eric38_1_6_0, "x86_64", false) => "bindings_eric_38_1_6_0_linux_x86_64.rs",
        (EricVersion::Eric39_6_4_0, "x86_64", false) => "bindings_eric_39_6_4_0_linux_x86_64.rs",
        (EricVersion::Eric40_1_8_0, "x86_64", false) => "bindings_eric_40_1_8_0_linux_x86_64.rs",
        (EricVersion::Eric40_2_10_0, "x86_64", false) => "bindings_eric_40_2_10_0_linux_x86_64.rs",
        _ => {
            panic!("Missing bindings for Eric version {eric_version} and target {target_arch}");
        }
    };

    println!("Select bindings for Eric version {eric_version} and target {target_arch}");

    let root_dir = std::env::var("CARGO_MANIFEST_DIR").expect("Set by cargo");
    let bindings_path = Path::new(&root_dir).join("bindings").join(bindings_file);

    std::fs::copy(bindings_path.clone(), bindings_target.clone()).unwrap_or_else(|_| {
        panic!(
            "Can't copy file from {} to {}",
            bindings_path.display(),
            bindings_target.display(),
        )
    });

    Ok(())
}

/// Generate bindings on-the-fly
#[cfg(feature = "generate-bindings")]
fn generate_bindings() -> io::Result<()> {
    let library_name =
        env::var("LIBRARY_NAME").expect("Missing environment variable 'LIBRARY_NAME'");
    let library_path =
        env::var("LIBRARY_PATH").expect("Missing environment variable 'LIBRARY_PATH'");
    let header_file = env::var("HEADER_FILE").expect("Missing environment variable 'HEADER_FILE'");

    let library_path = Path::new(&library_path);
    let header_file = Path::new(&header_file);

    println!("cargo:rustc-link-search={}", library_path.display());
    println!("cargo:rustc-link-lib={}", library_name);
    println!("cargo:rerun-if-changed={}", header_file.display());
    println!("cargo:rustc-env=LD_LIBRARY_PATH={}", library_path.display());

    let header = header_file.to_str().expect("Can't convert path to string");

    let bindings = bindgen::Builder::default()
        .header(header)
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        .generate()
        .expect("Can't generate bindings");

    let out_dir = env::var("OUT_DIR").expect("Can't read environment variable 'OUT_DIR'");
    let output_path = PathBuf::from(out_dir);

    bindings
        .write_to_file(output_path.join("bindings.rs"))
        .expect("Can't write bindings");

    Ok(())
}

/// Select latest bindings for documentation on docs.rs
#[cfg(feature = "docs-rs")]
fn select_bindings_for_docs_rs() -> io::Result<()> {
    let bindings_file = "bindings_eric_40_1_8_0_linux_x86_64.rs";

    let root_dir = std::env::var("CARGO_MANIFEST_DIR").expect("Set by cargo");
    let bindings_path = Path::new(&root_dir).join("bindings").join(bindings_file);

    let out_dir = env::var("OUT_DIR").expect("Can't read environment variable 'OUT_DIR'");
    let bindings_target = PathBuf::from(out_dir).join("bindings.rs");

    std::fs::copy(bindings_path.clone(), bindings_target.clone()).unwrap_or_else(|_| {
        panic!(
            "Can't copy file from {} to {}",
            bindings_path.display(),
            bindings_target.display(),
        )
    });
    Ok(())
}