blob: e698a47a968a8a133e22c2053a47465834136adc [file] [log] [blame]
extern crate libc;
#[macro_use]
extern crate lazy_static;
pub mod dynamic_library;
/// Error that can happen while loading the shared library.
#[derive(Debug, Clone)]
pub enum LoadingError {
///
LibraryNotFound {
descr: String,
},
/// One of the symbols could not be found in the library.
SymbolNotFound {
/// The symbol.
symbol: &'static str,
}
}
#[macro_export]
macro_rules! shared_library {
($struct_name:ident, pub $($rest:tt)+) => {
shared_library!(__impl $struct_name [] [] [] pub $($rest)+);
};
($struct_name:ident, fn $($rest:tt)+) => {
shared_library!(__impl $struct_name [] [] [] fn $($rest)+);
};
($struct_name:ident, static $($rest:tt)+) => {
shared_library!(__impl $struct_name [] [] [] static $($rest)+);
};
($struct_name:ident, $def_path:expr, $($rest:tt)+) => {
shared_library!(__impl $struct_name [] [$def_path] [] $($rest)+);
};
(__impl $struct_name:ident
[$($p1:tt)*] [$($p2:tt)*] [$($p3:tt)*]
, $($rest:tt)*
) => {
shared_library!(__impl $struct_name [$($p1)*] [$($p2)*] [$($p3)*] $($rest)*);
};
(__impl $struct_name:ident
[$($p1:tt)*] [$($p2:tt)*] [$($p3:tt)*]
pub $($rest:tt)*
) => {
shared_library!(__impl $struct_name
[$($p1)*] [$($p2)*] [$($p3)* pub] $($rest)*);
};
(__impl $struct_name:ident
[$($p1:tt)*] [$($p2:tt)*] [$($p3:tt)*]
fn $name:ident($($p:ident:$ty:ty),*) -> $ret:ty, $($rest:tt)*
) => {
shared_library!(__impl $struct_name
[$($p1)*, $name:unsafe extern fn($($p:$ty),*) -> $ret]
[$($p2)*]
[$($p3)*
unsafe fn $name($($p:$ty),*) -> $ret {
#![allow(dead_code)]
($struct_name::get_static_ref().$name)($($p),*)
}
] $($rest)*);
};
(__impl $struct_name:ident
[$($p1:tt)*] [$($p2:tt)*] [$($p3:tt)*]
static $name:ident:$ty:ty, $($rest:tt)*
) => {
shared_library!(__impl $struct_name
[$($p1)*, $name: $ty]
[$($p2)*]
[$($p3)*] $($rest)*);
};
(__impl $struct_name:ident
[$($p1:tt)*] [$($p2:tt)*] [$($p3:tt)*]
fn $name:ident($($p:ident:$ty:ty),*), $($rest:tt)*
) => {
shared_library!(__impl $struct_name
[$($p1)*] [$($p2)*] [$($p3)*]
fn $name($($p:$ty),*) -> (), $($rest)*);
};
(__impl $struct_name:ident [$(,$mem_n:ident:$mem_t:ty)+] [$($p2:tt)*] [$($p3:tt)*]) => {
/// Symbols loaded from a shared library.
#[allow(non_snake_case)]
pub struct $struct_name {
_library_guard: $crate::dynamic_library::DynamicLibrary,
$(
pub $mem_n: $mem_t,
)+
}
impl $struct_name {
/// Tries to open the dynamic library.
#[allow(non_snake_case)]
pub fn open(path: &::std::path::Path) -> Result<$struct_name, $crate::LoadingError> {
use std::mem;
let dylib = match $crate::dynamic_library::DynamicLibrary::open(Some(path)) {
Ok(l) => l,
Err(reason) => return Err($crate::LoadingError::LibraryNotFound { descr: reason })
};
$(
let $mem_n: *mut () = match unsafe { dylib.symbol(stringify!($mem_n)) } {
Ok(s) => s,
Err(_) => return Err($crate::LoadingError::SymbolNotFound { symbol: stringify!($mem_n) }),
};
)+
Ok($struct_name {
_library_guard: dylib,
$(
$mem_n: unsafe { mem::transmute($mem_n) },
)+
})
}
}
shared_library!(__write_static_fns $struct_name [] [$($p2)*] [$($p3)*]);
};
(__write_static_fns $struct_name:ident [$($p1:tt)*] [] [$($p3:tt)*]) => {
};
(__write_static_fns $struct_name:ident [$($p1:tt)*] [$defpath:expr] [$($standalones:item)+]) => {
impl $struct_name {
/// This function is used by the regular functions.
fn get_static_ref() -> &'static $struct_name {
$struct_name::try_loading().ok()
.expect(concat!("Could not open dynamic \
library `", stringify!($struct_name),
"`"))
}
/// Try loading the static symbols linked to this library.
pub fn try_loading() -> Result<&'static $struct_name, $crate::LoadingError> {
use std::sync::{Mutex, Once, ONCE_INIT};
use std::mem;
unsafe {
static mut DATA: *const Mutex<Option<$struct_name>> = 0 as *const _;
static mut INIT: Once = ONCE_INIT;
INIT.call_once(|| {
let data = Box::new(Mutex::new(None));
DATA = &*data;
mem::forget(data);
});
let data: &Mutex<Option<$struct_name>> = &*DATA;
let mut data = data.lock().unwrap();
if let Some(ref data) = *data {
return Ok(mem::transmute(data));
}
let path = ::std::path::Path::new($defpath);
let result = try!($struct_name::open(path));
*data = Some(result);
Ok(mem::transmute(data.as_ref().unwrap()))
}
}
}
$($standalones)+
};
}