1 extern crate libc;
2 
3 #[macro_use]
4 extern crate lazy_static;
5 
6 pub mod dynamic_library;
7 
8 /// Error that can happen while loading the shared library.
9 #[derive(Debug, Clone)]
10 pub enum LoadingError {
11     ///
12     LibraryNotFound {
13         descr: String,
14     },
15 
16     /// One of the symbols could not be found in the library.
17     SymbolNotFound {
18         /// The symbol.
19         symbol: &'static str,
20     }
21 }
22 
23 #[macro_export]
24 macro_rules! shared_library {
25     ($struct_name:ident, pub $($rest:tt)+) => {
26         shared_library!(__impl $struct_name [] [] [] pub $($rest)+);
27     };
28 
29     ($struct_name:ident, fn $($rest:tt)+) => {
30         shared_library!(__impl $struct_name [] [] [] fn $($rest)+);
31     };
32 
33     ($struct_name:ident, static $($rest:tt)+) => {
34         shared_library!(__impl $struct_name [] [] [] static $($rest)+);
35     };
36 
37     ($struct_name:ident, $def_path:expr, $($rest:tt)+) => {
38         shared_library!(__impl $struct_name [] [$def_path] [] $($rest)+);
39     };
40 
41     (__impl $struct_name:ident
42             [$($p1:tt)*] [$($p2:tt)*] [$($p3:tt)*]
43             , $($rest:tt)*
44     ) => {
45         shared_library!(__impl $struct_name [$($p1)*] [$($p2)*] [$($p3)*] $($rest)*);
46     };
47 
48     (__impl $struct_name:ident
49             [$($p1:tt)*] [$($p2:tt)*] [$($p3:tt)*]
50             pub $($rest:tt)*
51     ) => {
52         shared_library!(__impl $struct_name
53                        [$($p1)*] [$($p2)*] [$($p3)* pub] $($rest)*);
54     };
55 
56     (__impl $struct_name:ident
57             [$($p1:tt)*] [$($p2:tt)*] [$($p3:tt)*]
58             fn $name:ident($($p:ident:$ty:ty),*) -> $ret:ty, $($rest:tt)*
59     ) => {
60         shared_library!(__impl $struct_name
61                        [$($p1)*, $name:unsafe extern fn($($p:$ty),*) -> $ret]
62                        [$($p2)*]
63                        [$($p3)*
64                            unsafe fn $name($($p:$ty),*) -> $ret {
65                                #![allow(dead_code)]
66                                ($struct_name::get_static_ref().$name)($($p),*)
67                            }
68                         ] $($rest)*);
69     };
70 
71     (__impl $struct_name:ident
72             [$($p1:tt)*] [$($p2:tt)*] [$($p3:tt)*]
73             static $name:ident:$ty:ty, $($rest:tt)*
74     ) => {
75         shared_library!(__impl $struct_name
76                        [$($p1)*, $name: $ty]
77                        [$($p2)*]
78                        [$($p3)*] $($rest)*);
79     };
80 
81     (__impl $struct_name:ident
82             [$($p1:tt)*] [$($p2:tt)*] [$($p3:tt)*]
83             fn $name:ident($($p:ident:$ty:ty),*), $($rest:tt)*
84     ) => {
85         shared_library!(__impl $struct_name
86                        [$($p1)*] [$($p2)*] [$($p3)*]
87                        fn $name($($p:$ty),*) -> (), $($rest)*);
88     };
89 
90     (__impl $struct_name:ident [$(,$mem_n:ident:$mem_t:ty)+] [$($p2:tt)*] [$($p3:tt)*]) => {
91         /// Symbols loaded from a shared library.
92         #[allow(non_snake_case)]
93         pub struct $struct_name {
94             _library_guard: $crate::dynamic_library::DynamicLibrary,
95             $(
96                 pub $mem_n: $mem_t,
97             )+
98         }
99 
100         impl $struct_name {
101             /// Tries to open the dynamic library.
102             #[allow(non_snake_case)]
103             pub fn open(path: &::std::path::Path) -> Result<$struct_name, $crate::LoadingError> {
104                 use std::mem;
105 
106                 let dylib = match $crate::dynamic_library::DynamicLibrary::open(Some(path)) {
107                     Ok(l) => l,
108                     Err(reason) => return Err($crate::LoadingError::LibraryNotFound { descr: reason })
109                 };
110 
111                 $(
112                     let $mem_n: *mut () = match unsafe { dylib.symbol(stringify!($mem_n)) } {
113                         Ok(s) => s,
114                         Err(_) => return Err($crate::LoadingError::SymbolNotFound { symbol: stringify!($mem_n) }),
115                     };
116                 )+
117 
118                 Ok($struct_name {
119                     _library_guard: dylib,
120                     $(
121                         $mem_n: unsafe { mem::transmute($mem_n) },
122                     )+
123                 })
124             }
125         }
126 
127         shared_library!(__write_static_fns $struct_name [] [$($p2)*] [$($p3)*]);
128     };
129 
130     (__write_static_fns $struct_name:ident [$($p1:tt)*] [] [$($p3:tt)*]) => {
131     };
132 
133     (__write_static_fns $struct_name:ident [$($p1:tt)*] [$defpath:expr] [$($standalones:item)+]) => {
134         impl $struct_name {
135             /// This function is used by the regular functions.
136             fn get_static_ref() -> &'static $struct_name {
137                 $struct_name::try_loading().ok()
138                                            .expect(concat!("Could not open dynamic \
139                                                             library `", stringify!($struct_name),
140                                                             "`"))
141             }
142 
143             /// Try loading the static symbols linked to this library.
144             pub fn try_loading() -> Result<&'static $struct_name, $crate::LoadingError> {
145                 use std::sync::{Mutex, Once, ONCE_INIT};
146                 use std::mem;
147 
148                 unsafe {
149                     static mut DATA: *const Mutex<Option<$struct_name>> = 0 as *const _;
150 
151                     static mut INIT: Once = ONCE_INIT;
152                     INIT.call_once(|| {
153                         let data = Box::new(Mutex::new(None));
154                         DATA = &*data;
155                         mem::forget(data);
156                     });
157 
158                     let data: &Mutex<Option<$struct_name>> = &*DATA;
159                     let mut data = data.lock().unwrap();
160 
161                     if let Some(ref data) = *data {
162                         return Ok(mem::transmute(data));
163                     }
164 
165                     let path = ::std::path::Path::new($defpath);
166                     let result = try!($struct_name::open(path));
167                     *data = Some(result);
168                     Ok(mem::transmute(data.as_ref().unwrap()))
169                 }
170             }
171         }
172 
173         $($standalones)+
174     };
175 }
176