<file_sep>const fn get_flatc_name() -> &'static str {
    #[cfg(target_os = "windows")]
    return "flatc.exe";
    #[cfg(target_os = "macos")]
    return "flatc_osx";
    #[cfg(not(any(target_os = "windows", target_os = "macos")))]
    return "flatc_linux";
}

fn main() {
    let mut path = std::env::current_dir().unwrap();
    path.push("..");
    path.push(get_flatc_name());
    println!("cargo:rustc-env=FLATC={}", path.display());
}
<file_sep>[package]
name = "flatbuffers-run"
version = "0.1.0"
authors = ["<NAME> <<EMAIL>>"]
edition = "2018"

[dependencies]
bitflags = "1.2.1"<file_sep>[package]
name = "flatbuffers_builder"
version = "0.1.0"
authors = ["<NAME> <<EMAIL>>"]
edition = "2018"

[dependencies]
flatbuffers-run = { path = "../flatbuffers-run" }
flatbuffers = "0.6.0"

[build-dependencies]
flatbuffers-run = { path = "../flatbuffers-run" }
<file_sep>[package]
name = "flatbuffers-build"
version = "0.1.0"
authors = ["<NAME> <<EMAIL>>"]
edition = "2018"

[dependencies]
flatbuffers_builder = { path = "./flatbuffers-builder" }

[workspace]
members = [
    "flatbuffers-builder",
    "flatbuffers-run",
]<file_sep>extern crate flatbuffers_run;

fn main() {
    let out_dir = std::env::current_dir().unwrap();
    if out_dir.join("reflection_generated.rs").exists() {
        return;
    }

    if let Ok(src_path) = std::env::var("FBS_REFLECTION_SRC") {
        let mut runner = flatbuffers_run::Runner::new();
        runner
            .rust(true)
            .add_definition(&src_path)
            .out_dir(&out_dir);
        runner.compile().expect("Failed to compile fbs reflection");
    } else {
        panic!("Faild to acquire reflection source. Specify this with FBS_REFLECTION_SRC env var");
    }
}
<file_sep>#[path = "../reflection_generated.rs"]
pub mod fbs_schema;
use std::ffi::OsStr;

pub trait ServiceGenerator {
    fn write_service<'a>(
        &mut self,
        writer: &mut dyn std::io::Write,
        schema: fbs_schema::reflection::Schema<'a>,
    ) -> std::io::Result<()>;
}

pub struct Builder {
    runner: flatbuffers_run::Runner,
    generator: Option<Box<dyn ServiceGenerator>>,
}

impl Builder {
    pub fn new<T: ?Sized + AsRef<OsStr>>(out_dir: &T) -> Self {
        Builder {
            runner: {
                let mut runner = flatbuffers_run::Runner::new();
                runner.out_dir(out_dir);

                runner
            },
            generator: None,
        }
    }

    pub fn add_definition<T: ?Sized + AsRef<OsStr>>(&mut self, definition: &T) -> &mut Self {
        self.runner.add_definition(definition);

        self
    }

    pub fn add_definitions<T: ?Sized + AsRef<OsStr>>(&mut self, definitions: &[&T]) -> &mut Self {
        self.runner.add_definitions(definitions);

        self
    }

    pub fn add_include<T: ?Sized + AsRef<OsStr>>(&mut self, include: &T) -> &mut Self {
        self.runner.add_include(include);

        self
    }

    pub fn add_includes<T: ?Sized + AsRef<OsStr>>(&mut self, includes: &[&T]) -> &mut Self {
        self.runner.add_includes(includes);

        self
    }

    pub fn generator(&mut self, generator: Box<dyn ServiceGenerator>) -> &mut Self {
        self.generator = Some(generator);

        self
    }

    pub fn generate(mut self) -> std::io::Result<()> {
        self.runner.rust(true);

        if self.generator.is_some() {
            self.runner.schema(true);
        }

        for def in self.runner.get_definitions() {
            println!("cargo:rerun-if-changed={}", def.to_str().unwrap());
        }
        let generateds = self.runner.compile()?;
        if self.generator.is_none() {
            return Ok(());
        }

        let mut generator = self.generator.unwrap();
        let mut schema_buffer: Vec<u8> = Default::default();
        for item in generateds {
            use std::fs::{File, OpenOptions};
            use std::io::{Read, Write};

            let schema_path = item.schema.unwrap();
            let schema = {
                let mut schema = File::open(schema_path.clone())?;
                schema.read_to_end(&mut schema_buffer)?;

                fbs_schema::reflection::get_root_as_schema(&schema_buffer)
            };

            if schema.services().is_none() {
                continue;
            }

            let mut src = {
                let src_path = item.rust.unwrap();
                let mut src = OpenOptions::new();

                src.append(true).open(src_path)?
            };

            writeln!(src)?;

            generator.write_service(&mut src, schema)?;
            src.flush()?;

            drop(src);
            std::fs::remove_file(schema_path)?;
        }

        Ok(())
    }
}
<file_sep>extern crate bitflags;
use bitflags::bitflags;
use std::ffi::OsStr;
use std::path::PathBuf;

const FLATC_PATH: &'static str = env!("FLATC");

bitflags! {
    pub struct CompileFlags: u32 {
        const RUST =   0b00000001;
        const SCHEMA = 0b00000010;
    }
}

pub struct Runner {
    flags: CompileFlags,
    definitions: Vec<PathBuf>,
    includes: Vec<PathBuf>,
    out_dir: Option<PathBuf>,
}

pub struct CompileResult {
    pub rust: Option<PathBuf>,
    pub schema: Option<PathBuf>,
}

impl Runner {
    pub fn new() -> Self {
        Runner {
            flags: CompileFlags::empty(),
            definitions: Default::default(),
            includes: Default::default(),
            out_dir: None,
        }
    }

    pub fn get_definitions(&self) -> &[PathBuf] {
        self.definitions.as_slice()
    }

    pub fn add_definition<T: ?Sized + AsRef<OsStr>>(&mut self, definition: &T) -> &mut Self {
        self.definitions.push(PathBuf::from(definition));

        self
    }

    pub fn add_definitions<T: ?Sized + AsRef<OsStr>>(&mut self, definitions: &[&T]) -> &mut Self {
        self.definitions
            .extend(definitions.iter().map(|s| PathBuf::from(*s)));

        self
    }

    pub fn add_include<T: ?Sized + AsRef<OsStr>>(&mut self, include: &T) -> &mut Self {
        self.includes.push(PathBuf::from(include));

        self
    }

    pub fn add_includes<T: ?Sized + AsRef<OsStr>>(&mut self, includes: &[&T]) -> &mut Self {
        self.includes
            .extend(includes.iter().map(|s| PathBuf::from(*s)));

        self
    }

    pub fn out_dir<T: ?Sized + AsRef<OsStr>>(&mut self, dir: &T) -> &mut Self {
        self.out_dir = Some(PathBuf::from(dir));

        self
    }

    pub fn rust(&mut self, enable: bool) -> &mut Self {
        self.flags.set(CompileFlags::RUST, enable);
        self
    }

    pub fn schema(&mut self, enable: bool) -> &mut Self {
        self.flags.set(CompileFlags::SCHEMA, enable);
        self
    }

    pub fn compile(self) -> std::io::Result<Vec<CompileResult>> {
        use std::process::Command;

        let mut command = Command::new(FLATC_PATH);
        let gen_rust = self.flags.contains(CompileFlags::RUST);
        if gen_rust {
            command.arg("--rust");
        }
        let gen_schema = self.flags.contains(CompileFlags::SCHEMA);
        if gen_schema {
            command.args(&["--schema", "--binary", "--bfbs-comments", "--bfbs-builtins"]);
        }
        let out_dir = if let Some(out_dir) = self.out_dir {
            command.arg("-o");
            command.arg(&out_dir);
            out_dir
        } else {
            std::env::current_dir().unwrap().to_owned()
        };

        for definition in &self.definitions {
            command.arg(definition);
        }

        let mut child = command.spawn()?;
        child.wait()?;

        Ok(self
            .definitions
            .iter()
            .map(|def| {
                let file_name = std::path::Path::new(&def).file_stem().unwrap();
                let file_name = file_name.to_str().unwrap();
                CompileResult {
                    rust: if gen_rust {
                        Some(out_dir.join(format!("{}_generated.rs", file_name)))
                    } else {
                        None
                    },
                    schema: if gen_schema {
                        Some(out_dir.join(format!("{}.bfbs", file_name)))
                    } else {
                        None
                    },
                }
            })
            .collect())
    }
}
<file_sep>extern crate flatbuffers_builder;

pub use flatbuffers_builder::*;
