mujoco-rs 0.4.0

A Rust wrapper around the MuJoCo C library, with a native simple viewer (re-)written in Rust.
Documentation
use bindgen::callbacks::TypeKind;
use bindgen::callbacks::{ParseCallbacks, DeriveInfo};
use std::path::PathBuf;
use std::env;
use std::fs;


#[derive(Debug)]
struct CloneCallback;

impl ParseCallbacks for CloneCallback {
    fn add_derives(&self, info: &DeriveInfo) -> Vec<String> {
        let mut data = vec!["Clone".into()];
        if info.name.starts_with("mjui") || info.name == "mjrRect_" {
            data.push("Copy".into());
        }

        if info.kind == TypeKind::Enum {
            data.push("TryFromPrimitive".into());
        }

        data
    }
}



fn main() {
    /// Environmental variable which contains the path to the MuJoCo's build/lib/ directory.
    /// This mean for static linking, otherwise the dynamic MuJoCo library can just be installed and used.
    const MUJOCO_STATIC_LIB_PATH_VAR: &str = "MUJOCO_STATIC_LINK_LIB";
    const MUJOCO_DYN_LIB_PATH_VAR: &str = "MUJOCO_DYNAMIC_LINK_LIB";
    const GENERATE_FFI: &str = "GENERATE_FFI";

    println!("cargo:rerun-if-env-changed={MUJOCO_STATIC_LIB_PATH_VAR}");
    println!("cargo:rerun-if-env-changed={MUJOCO_DYN_LIB_PATH_VAR}");
    println!("cargo:rerun-if-env-changed={GENERATE_FFI}");

    let mujoco_lib_path= env::var(MUJOCO_STATIC_LIB_PATH_VAR);

    let generate_ffi = env::var(GENERATE_FFI).map_or(false, |v| v == "y" || v == "1");
    if !generate_ffi {
        return;
    }

    /* Static linking */
    if let Ok(path) = mujoco_lib_path {
        let mj_lib_pathbuf = PathBuf::from(path);
        let mj_lib_simulate_path = mj_lib_pathbuf.join("libsimulate.a");

        println!("cargo::rerun-if-changed={}", mj_lib_simulate_path.canonicalize().unwrap().display());
        println!("cargo:rustc-link-search={}", mj_lib_pathbuf.canonicalize().unwrap().display());

        #[cfg(feature = "cpp-viewer")]
        println!("cargo:rustc-link-lib=simulate");

        println!("cargo:rustc-link-lib=mujoco");
        println!("cargo:rustc-link-lib=lodepng");
        println!("cargo:rustc-link-lib=tinyxml2");
        println!("cargo:rustc-link-lib=qhullstatic_r");
        println!("cargo:rustc-link-lib=ccd");

        if cfg!(unix) {
            println!("cargo:rustc-link-lib=stdc++");
        }
    }

    /* Dynamic linking */
    else {
        let mujoco_dylib_path = PathBuf::from(env::var(MUJOCO_DYN_LIB_PATH_VAR)
            .unwrap_or_else(|_| panic!("nor the static library path ({MUJOCO_STATIC_LIB_PATH_VAR}),\
                nor the dynamic library path ({MUJOCO_DYN_LIB_PATH_VAR}) was given.")));

        println!("cargo:rustc-link-search={}", mujoco_dylib_path.canonicalize().unwrap().display());
        println!("cargo:rustc-link-lib=mujoco");
    }

    let output_path = PathBuf::from("./src/");
    let current_dir = env::current_dir().unwrap();
    let include_dir_mujoco = current_dir.join("src/cpp/include/mujoco");
    let include_dir_simulate = include_dir_mujoco.join("viewer");

    // Generate MuJoCo bindings
    let bindings_mujoco = bindgen::Builder::default()
        .header(include_dir_mujoco.join("mujoco.h").to_str().unwrap())
        .header(include_dir_simulate.join("simulate.hpp").to_str().unwrap())
        .header(include_dir_simulate.join("glfw_dispatch.hpp").to_str().unwrap())
        .clang_arg("-std=c++20")
        .clang_arg("-stdlib=libc++")
        .clang_arg(format!("-I{}", current_dir.join("mujoco/build/_deps/glfw3-src/include/").display()))
        .blocklist_item("std::tuple.*")
        .allowlist_item("mj.*")
        .allowlist_item("mujoco::.*")
        .allowlist_item("new_simulate")
        .allowlist_item("free_simulate")
        .layout_tests(false)
        .derive_default(false)
        .opaque_type("std::.*")
        .derive_copy(false)
        .rustified_enum(".*")
        .parse_callbacks(Box::new(CloneCallback))
        .generate()
        .expect("unable to generate MuJoCo bindings");

    let outputfile_dir = output_path.join("mujoco_c.rs");
    let mut fdata = bindings_mujoco
        .to_string();

    // Extra adjustments
    fdata = fdata.replace("pub __lx: std_basic_string_value_type<_CharT>,", "pub __lx: std::mem::ManuallyDrop<std_basic_string_value_type<_CharT>>,");
    let re = regex::Regex::new(r"#\[derive\((.*?Clone.*?), Clone, (.*?)\)\]").unwrap();
    fdata = re.replace_all(&fdata, "#[derive($1, $2)]").to_string();
    fdata = "use num_enum::TryFromPrimitive;\n\n".to_string() + &fdata;

    fs::write(outputfile_dir, fdata).unwrap();
}