selium-remote-client-protocol 0.3.0

Selium module for supporting remote clients
use std::{ffi::OsStr, fs, path::Path};

use flatbuffers_build::BuilderOptions;
use flatc_fork::flatc;

const SCHEMAS: [&str; 1] = ["schemas/remote_client.fbs"];

fn main() {
    println!("cargo::rerun-if-changed=schemas/");

    BuilderOptions::new_with_files(SCHEMAS)
        .set_output_path("src/fbs/")
        .set_compiler(flatc().to_str().expect("Non UTF-8 path to flatc binary"))
        .compile()
        .expect("flatbuffer compilation failed");

    rewrite_module_root("src/fbs").expect("failed to rewrite module root file");
}

fn rewrite_module_root(root: impl AsRef<Path>) -> std::io::Result<()> {
    let root = root.as_ref();
    let remote_client_dir = root.join("remote_client");
    let mut namespaces = list_dirs(&remote_client_dir)?;
    namespaces.sort();

    let mut content =
        String::from("// Automatically generated by build.rs. Do not modify manually.\n");
    content.push_str("// Combined module tree for remote-client Flatbuffers namespaces.\n");
    content.push_str("pub mod remote_client {\n");
    content.push_str("  use super::*;\n");

    for ns in namespaces {
        let dir = remote_client_dir.join(&ns);
        let mut files = list_rs_files(&dir)?;
        files.sort();
        content.push_str(&format!("  pub mod {} {{\n    use super::*;\n", ns));
        for file in files {
            content.push_str(&format!(
                "    mod {};\n    pub use self::{}::*;\n",
                file, file
            ));
        }
        content.push_str("  }\n");
    }

    content.push_str("}\n");
    fs::write(root.join("mod.rs"), content)
}

fn list_dirs(dir: &Path) -> std::io::Result<Vec<String>> {
    let mut names = Vec::new();
    if dir.exists() {
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            if entry.file_type()?.is_dir()
                && let Some(name) = entry.file_name().to_str()
            {
                names.push(name.to_string());
            }
        }
    }
    Ok(names)
}

fn list_rs_files(dir: &Path) -> std::io::Result<Vec<String>> {
    let mut names = Vec::new();
    if dir.exists() {
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            if entry.file_type()?.is_file()
                && entry.path().extension() == Some(OsStr::new("rs"))
                && let Some(stem) = entry.path().file_stem().and_then(|s| s.to_str())
            {
                names.push(stem.to_string());
            }
        }
    }
    Ok(names)
}