#[cfg(feature="runtime")]
macro_rules! link {
(@LOAD: #[cfg($cfg:meta)] fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*) => (
#[cfg($cfg)]
pub fn $name(library: &mut super::SharedLibrary) {
let symbol = unsafe { library.library.get(stringify!($name).as_bytes()) }.ok();
library.functions.$name = match symbol {
Some(s) => *s,
None => None,
};
}
#[cfg(not($cfg))]
pub fn $name(_: &mut super::SharedLibrary) {}
);
(@LOAD: fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*) => (
link!(@LOAD: #[cfg(feature="runtime")] fn $name($($pname: $pty), *) $(-> $ret)*);
);
($($(#[cfg($cfg:meta)])* pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*;)+) => (
use std::cell::{RefCell};
use std::sync::{Arc};
/// The set of functions loaded dynamically.
#[derive(Debug, Default)]
pub struct Functions {
$($(#[cfg($cfg)])* pub $name: Option<unsafe extern fn($($pname: $pty), *) $(-> $ret)*>,)+
}
#[derive(Debug)]
pub struct SharedLibrary {
library: libloading::Library,
pub functions: Functions,
}
impl SharedLibrary {
fn new(library: libloading::Library) -> SharedLibrary {
SharedLibrary { library: library, functions: Functions::default() }
}
}
thread_local!(static LIBRARY: RefCell<Option<Arc<SharedLibrary>>> = RefCell::new(None));
pub fn is_loaded() -> bool {
LIBRARY.with(|l| l.borrow().is_some())
}
fn with_library<T, F>(f: F) -> Option<T> where F: FnOnce(&SharedLibrary) -> T {
LIBRARY.with(|l| {
match l.borrow().as_ref() {
Some(library) => Some(f(&library)),
_ => None,
}
})
}
$(
#[cfg_attr(feature="cargo-clippy", allow(too_many_arguments))]
$(#[cfg($cfg)])*
pub unsafe fn $name($($pname: $pty), *) $(-> $ret)* {
let f = with_library(|l| {
match l.functions.$name {
Some(f) => f,
_ => panic!(concat!("function not loaded: ", stringify!($name))),
}
}).expect("a `libclang` shared library is not loaded on this thread");
f($($pname), *)
}
$(#[cfg($cfg)])*
pub mod $name {
pub fn is_loaded() -> bool {
super::with_library(|l| l.functions.$name.is_some()).unwrap_or(false)
}
}
)+
mod load {
$(link!(@LOAD: $(#[cfg($cfg)])* fn $name($($pname: $pty), *) $(-> $ret)*);)+
}
pub fn load_manually() -> Result<SharedLibrary, String> {
#[path="../build.rs"]
mod build;
let file = try!(build::find_shared_library());
let library = libloading::Library::new(&file).map_err(|e| {
format!("the `libclang` shared library at {} could not be opened: {}", file.display(), e)
});
let mut library = SharedLibrary::new(try!(library));
$(load::$name(&mut library);)+
Ok(library)
}
#[allow(dead_code)]
pub fn load() -> Result<(), String> {
let library = Arc::new(try!(load_manually()));
LIBRARY.with(|l| *l.borrow_mut() = Some(library));
Ok(())
}
pub fn unload() -> Result<(), String> {
let library = set_library(None);
if library.is_some() {
Ok(())
} else {
Err("a `libclang` shared library is not in use in the current thread".into())
}
}
pub fn get_library() -> Option<Arc<SharedLibrary>> {
LIBRARY.with(|l| l.borrow_mut().clone())
}
pub fn set_library(library: Option<Arc<SharedLibrary>>) -> Option<Arc<SharedLibrary>> {
LIBRARY.with(|l| mem::replace(&mut *l.borrow_mut(), library))
}
)
}
#[cfg(not(feature="runtime"))]
macro_rules! link {
($($(#[cfg($cfg:meta)])* pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*;)+) => (
extern { $($(#[cfg($cfg)])* pub fn $name($($pname: $pty), *) $(-> $ret)*;)+ }
$($(#[cfg($cfg)])*
pub mod $name {
pub fn is_loaded() -> bool { true }
})+
)
}