use bindgen::{Builder as BindgenBuilder, BindgenError, CargoCallbacks};
use bindgen::callbacks::ParseCallbacks;
use std::boxed::Box;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::convert::{AsRef, Into};
use std::default::Default;
use std::io::{Error as StdIoError, ErrorKind as StdIoErrorKind, Result as StdIoResult};
use std::path::{Path, PathBuf};
pub trait BindgenBuilderContext
where Self: Default
{
fn emulate_generate_error_set(&mut self, emulate_generate_error: bool) -> &mut Self;
fn emulate_generate_error_get(&self) -> bool;
fn emulate_write_error_set(&mut self, emulate_generate_error: bool) -> &mut Self;
fn emulate_write_error_get(&self) -> bool;
}
#[derive(Debug)]
struct BindgenBuilderContextTLS
{
emulate_generate_error: bool,
emulate_write_error: bool,
}
impl Default for BindgenBuilderContextTLS
{
fn default() -> Self
{
BindgenBuilderContextTLS {
emulate_generate_error: false,
emulate_write_error: false,
}
}
}
impl BindgenBuilderContext for BindgenBuilderContextTLS
{
fn emulate_generate_error_set(&mut self, emulate_generate_error: bool) -> &mut Self
{
self.emulate_generate_error = emulate_generate_error;
self
}
fn emulate_generate_error_get(&self) -> bool
{
self.emulate_generate_error
}
fn emulate_write_error_set(&mut self, emulate_write_error: bool) -> &mut Self
{
self.emulate_write_error = emulate_write_error;
self
}
fn emulate_write_error_get(&self) -> bool
{
self.emulate_write_error
}
}
thread_local!
{
static BINDGEN_BUILDER_CONTEXT_TLS: RefCell<BindgenBuilderContextTLS> =
RefCell::new(BindgenBuilderContextTLS::default());
}
#[derive(Debug)]
pub struct BindgenBuilderContextAccess
{
}
impl Default for BindgenBuilderContextAccess
{
fn default() -> Self
{
BindgenBuilderContextAccess {}
}
}
impl BindgenBuilderContext for BindgenBuilderContextAccess
{
fn emulate_generate_error_set(&mut self, emulate_generate_error: bool) -> &mut Self
{
BINDGEN_BUILDER_CONTEXT_TLS.with(|ctx|
{
ctx.borrow_mut().emulate_generate_error_set(emulate_generate_error);
});
self
}
fn emulate_generate_error_get(&self) -> bool
{
BINDGEN_BUILDER_CONTEXT_TLS.with(|ctx|
{
ctx.borrow().emulate_generate_error_get()
})
}
fn emulate_write_error_set(&mut self, emulate_write_error: bool) -> &mut Self
{
BINDGEN_BUILDER_CONTEXT_TLS.with(|ctx|
{
ctx.borrow_mut().emulate_write_error_set(emulate_write_error);
});
self
}
fn emulate_write_error_get(&self) -> bool
{
BINDGEN_BUILDER_CONTEXT_TLS.with(|ctx|
{
ctx.borrow().emulate_write_error_get()
})
}
}
#[derive(Debug)]
pub struct Builder
{
builder: BindgenBuilder,
header: Option<String>,
parse_callbacks: VecDeque<Box<dyn ParseCallbacks>>,
generate_block: bool,
generate_comments: bool,
}
#[derive(Debug)]
pub struct Bindings
{
pub builder: Builder,
write_to_file: RefCell<PathBuf>,
}
impl Builder
{
pub fn header<T: Into<String>>(mut self, header: T) -> Builder
{
let header_str = header.into();
self.builder = self.builder.header(header_str.clone());
self.header = Some(header_str);
self
}
pub fn parse_callbacks(mut self, cb: Box<dyn ParseCallbacks>) -> Self
{
self.parse_callbacks.push_back(cb);
self
}
pub fn generate_block(mut self, doit: bool) -> Self
{
self.builder = self.builder.generate_block(doit);
self.generate_block = doit;
self
}
pub fn generate_comments(mut self, doit: bool) -> Self
{
self.builder = self.builder.generate_comments(doit);
self.generate_comments = doit;
self
}
pub fn generate(mut self) -> Result<Bindings, BindgenError>
{
while let Some(cb) = self.parse_callbacks.pop_front() {
self.builder = self.builder.parse_callbacks(cb);
}
let bindgen_builder_ctx = BindgenBuilderContextAccess::default();
let emulate_error = bindgen_builder_ctx.emulate_generate_error_get();
if emulate_error {
Err(BindgenError::ClangDiagnostic("emulated by mock".to_string()))
} else {
Ok(Bindings::new(self))
}
}
}
impl Default for Builder
{
fn default() -> Self
{
Builder {
builder: BindgenBuilder::default(),
header: None,
parse_callbacks: VecDeque::new(),
generate_block: false,
generate_comments: false,
}
}
}
impl Bindings
{
fn new(builder: Builder) -> Self
{
Bindings {
builder: builder,
write_to_file: RefCell::new(PathBuf::new()),
}
}
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> StdIoResult<()>
{
self.write_to_file.borrow_mut().push(path);
let bindgen_builder_ctx = BindgenBuilderContextAccess::default();
let emulate_error = bindgen_builder_ctx.emulate_write_error_get();
if emulate_error {
Err(StdIoError::new(StdIoErrorKind::Other, "emulated by mock"))
} else {
Ok(())
}
}
}
mod tests {
use super::*;
#[test]
fn test_struct_bindgen_builder_context()
{
let mut bindgen_builder_ctx = BindgenBuilderContextAccess::default();
BINDGEN_BUILDER_CONTEXT_TLS.with( |ctx|
{
assert_eq!(ctx.borrow().emulate_generate_error, false);
assert_eq!(ctx.borrow().emulate_write_error, false);
});
assert_eq!(bindgen_builder_ctx.emulate_generate_error_get(), false);
assert_eq!(bindgen_builder_ctx.emulate_write_error_get(), false);
bindgen_builder_ctx.emulate_generate_error_set(true);
BINDGEN_BUILDER_CONTEXT_TLS.with(|ctx|
{
assert_eq!(ctx.borrow().emulate_generate_error, true);
assert_eq!(ctx.borrow().emulate_write_error, false);
});
assert_eq!(bindgen_builder_ctx.emulate_generate_error_get(), true);
assert_eq!(bindgen_builder_ctx.emulate_write_error_get(), false);
bindgen_builder_ctx.emulate_write_error_set(true);
BINDGEN_BUILDER_CONTEXT_TLS.with(|ctx|
{
assert_eq!(ctx.borrow().emulate_generate_error, true);
assert_eq!(ctx.borrow().emulate_write_error, true);
});
assert_eq!(bindgen_builder_ctx.emulate_generate_error_get(), true);
assert_eq!(bindgen_builder_ctx.emulate_write_error_get(), true);
bindgen_builder_ctx.emulate_generate_error_set(false);
BINDGEN_BUILDER_CONTEXT_TLS.with(|ctx|
{
assert_eq!(ctx.borrow().emulate_generate_error, false);
assert_eq!(ctx.borrow().emulate_write_error, true);
});
assert_eq!(bindgen_builder_ctx.emulate_generate_error_get(), false);
assert_eq!(bindgen_builder_ctx.emulate_write_error_get(), true);
bindgen_builder_ctx.emulate_write_error_set(false);
BINDGEN_BUILDER_CONTEXT_TLS.with(|ctx|
{
assert_eq!(ctx.borrow().emulate_generate_error, false);
assert_eq!(ctx.borrow().emulate_write_error, false);
});
assert_eq!(bindgen_builder_ctx.emulate_generate_error_get(), false);
assert_eq!(bindgen_builder_ctx.emulate_write_error_get(), false);
BINDGEN_BUILDER_CONTEXT_TLS.with(|ctx|
{
println!("bindgen_builder_ctx (TLS) = {:?}.", ctx.borrow());
});
println!("bindgen_builder_ctx (Access) = {:?}.", bindgen_builder_ctx);
}
#[test]
fn test_struct_builder()
{
let mut builder = Builder::default();
assert!(builder.header.is_none());
let header_filename = "hello_world_c_exported_to_rush.h";
builder = builder.header(String::from(header_filename));
assert_eq!(builder.header, Some(String::from(header_filename)));
assert_eq!(builder.parse_callbacks.len(), 0);
builder = builder.parse_callbacks(Box::new(CargoCallbacks));
assert_eq!(builder.parse_callbacks.len(), 1);
println!("builder = {:?}.", builder);
let bindings = builder.generate().unwrap();
let mut binding_pathbuf = PathBuf::new();
binding_pathbuf.push(header_filename);
binding_pathbuf.set_extension("in");
bindings.write_to_file(&binding_pathbuf)
.expect("bindings.write_to_file MUST succeed");
assert_eq!(*(bindings.write_to_file.borrow()), binding_pathbuf);
println!("bindings = {:?}.", bindings);
}
}