vbox_raw 0.4.2

FFI bindings for Rust to VirtualBox
Documentation
use std::{env, fs};
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf;
use regex::Regex;

fn main() {
    bindgen_lib();
    link_lib();
    generate_errors();
}

fn bindgen_lib() {
    let c_dir = get_c_lib_dir();
    println!("bindgen_lib{:?}", c_dir);
    let c_dir = PathBuf::from(c_dir);
    let c_lib = c_dir.join("VBoxCAPIGlue.h");
    let bindings = bindgen::Builder::default()
        .header(c_lib.to_str().unwrap())
        .generate()
        .unwrap();
    let outfile = File::create(output().join("sys_lib.rs")).unwrap();
    bindings.write(Box::new(&outfile)).unwrap();
    outfile.sync_all().unwrap();
}

fn link_lib() {
    let c_dir = get_c_lib_dir();
    println!("bindgen_lib{:?}", c_dir);
    let c_dir = PathBuf::from(c_dir);
    let c_lib = c_dir.join("VBoxCAPIGlue.c");
    let src = [c_lib.to_str().unwrap()];
    let mut builder = cc::Build::new();
    let build = builder.files(src.iter()).include("include");
    build.compile("vboxapi");
}

fn generate_errors() {
    let ver_str = format!("pub const BUILD_VER: u32 = {};\n", get_ver());
    let c_dir = get_c_lib_dir();
    let dir_entries = fs::read_dir(c_dir.clone()).unwrap();
    let mut map = HashMap::new();
    for entry in dir_entries {
        match entry {
            Ok(entry) => {
                println!("{:#?}", entry.file_name().to_str().unwrap());
                let file = format!("{}/{}", c_dir.clone(), entry.file_name().to_str().unwrap());

                map.extend(parse_to_vec(&file));
            }
            Err(_) => {}
        };
    }
    let text = generate_file(map.clone());
    println!("{:#?}", text);
    println!("{:#?}", map.clone());
    let mut file = File::create(output().join("error_map.rs")).unwrap();
    file.write_all(ver_str.as_bytes()).unwrap();
    file.write_all(text.as_bytes()).unwrap();
    // panic!("{:#?}", map.clone());
}

fn generate_file(map: HashMap<u32, String>) -> String {
    let mut text = String::new();
    text.push_str("pub fn get_error_msg(error_code: u32) -> String {\n");
    text.push_str("    match error_code {\n");
    for (key, value) in &map {
        text.push_str(format!("            {} => \"{}\".to_string(),\n", key, value).as_str());
    }
    text.push_str("            _ => \"UNKNOWN\".to_string()\n");
    text.push_str("    }\n");
    text.push_str("}\n\n");
    text.push_str("pub fn get_error_code(error_msg: &str) -> u32 {\n");
    text.push_str("    match error_msg {\n");
    for (key, value) in &map {
        text.push_str(format!("            \"{}\" => {},\n", value, key).as_str());
    }
    text.push_str("            _ => 0\n");
    text.push_str("    }\n");
    text.push_str("}\n");
    text
}

fn parse_to_vec(header: &str) -> HashMap<u32, String> {
    let mut map = HashMap::new();

    if let Ok(file) = File::open(header) {
        let reader = BufReader::new(file);
        for line in reader.lines() {
            if let Ok(line) = line {
                if line.contains("(nsresult)") || line.contains("(HRESULT)") {
                    let re = Regex::new(r"\s+").unwrap();
                    let result = re.replace_all(&line, " ");
                    let words: Vec<&str> = result.split_whitespace().collect();
                    if words.len() > 2 {
                        let re = Regex::new(r"0x[\da-fA-F]+").unwrap();
                        if let Some(capture) = re.find(words.last().unwrap()) {
                            let value = capture.as_str();
                            if let Ok(value) = u32::from_str_radix(&value[2..], 16) {
                                map.insert(value, words.get(1).unwrap().to_string());
                            }
                        }
                    }
                }
            }
        }
    }
    map
}
fn output() -> PathBuf {
    PathBuf::from(env::var("OUT_DIR").unwrap())
}
fn get_c_lib_dir() -> String {
    if cfg!(feature = "v7_2") {
        "vendor/vbox_7_2".to_string()
    } else if cfg!(feature = "v7_1") {
        "vendor/vbox_7_1".to_string()
    } else if cfg!(feature = "v7_0") {
        "vendor/vbox_7_0".to_string()
    } else if cfg!(feature = "v6_1") {
        "vendor/vbox_6_1".to_string()
    } else {
        "vendor/vbox_7_2".to_string()
    }
}

fn get_ver() -> u32 {
    if cfg!(feature = "v7_2") {
        72
    } else if cfg!(feature = "v7_1") {
        71
    } else if cfg!(feature = "v7_0") {
        70
    } else if cfg!(feature = "v6_1") {
        61
    } else {
        72
    }
}