snek/
lib.rs

1//////////////////////////////////////////////////////////////////////////////
2//  File: rust-snek/lib.rs
3//////////////////////////////////////////////////////////////////////////////
4//  Copyright 2016 Samuel Sleight
5//
6//  Licensed under the Apache License, Version 2.0 (the "License");
7//  you may not use this file except in compliance with the License.
8//  You may obtain a copy of the License at
9//
10//      http://www.apache.org/licenses/LICENSE-2.0
11//
12//  Unless required by applicable law or agreed to in writing, software
13//  distributed under the License is distributed on an "AS IS" BASIS,
14//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//  See the License for the specific language governing permissions and
16//  limitations under the License.
17//////////////////////////////////////////////////////////////////////////////
18
19//! This library provides a convenient interface for loading dynamic libraries
20//! at runtime, and for retrieving symbols from them. The recommended use is
21//! via the [`snek!`](macro.snek!.html) macro, which defines a structure which
22//! loads symbols on construction and has methods for each one, however it is
23//! also possible to manually load libraries and symbols using a [`Snek`](struct.Snek.html)
24//! instance.
25//!
26//! # Safety
27//! There is no way of verifying the type of loaded symbols, so both methods of
28//! using them assume that the given type is correct - this library should be used
29//! very carefully. Consider everything very unstable at the moment.
30//!
31//! # Example
32//! ```
33//! #[macro_use] extern crate snek;
34//! extern crate libc;
35//!
36//! use libc::c_int;
37//!
38//! snek! {
39//!     Example {
40//!         hello: () -> (),
41//!         add: (x: c_int, y: c_int) -> c_int
42//!     }
43//! }
44//!
45//! fn main() {
46//!     if let Ok(example) = Example::load("libexample.so") {
47//!         unsafe { example.hello() };
48//!         println!("2 + 4 = {}", unsafe { example.add(2, 4) });
49//!     }
50//! }
51
52extern crate libc;
53
54#[cfg(windows)]
55extern crate winapi;
56#[cfg(windows)]
57extern crate kernel32;
58
59pub use snek::{Snek, load_library, load_symbol, drop_library};
60pub use symbol::Symbol;
61
62mod snek;
63mod symbol;
64
65/// This enum stores information about the error returned when loading a library
66/// or symbol fails. On unix platforms, it hold the result of `dlerror()`.
67#[derive(Debug)]
68pub enum Error {
69    LibraryLoadError(String),
70    SymbolLoadError(String)
71}
72
73/// This macro is used to generate a struct that wraps a dynamic library with
74/// generated loading code. Each defined function will be loaded as a symbol
75/// from the library when an instance of the struct is constructed, and can be
76/// called via functions of the same name attached to the struct.
77///
78/// In the same way as a [`Snek`](struct.Snek.html) instance, when an instance
79/// of a struct defined by this macro is dropped, the library is unloaded.
80///
81/// # Safety
82/// As with [`Symbol::with`](struct.Symbol.html#method.with), there is no way
83/// of verifying the types of the functions so care should be taken to ensure
84/// they are correct. All the imported functions will be unsafe as a result
85/// of this.
86///
87/// # Example
88/// This example loads the same function as given in the [`Snek`](struct.Snek.html)
89/// usage example:
90///
91/// ```
92/// # #[macro_use] extern crate snek;
93/// # extern crate libc;
94/// # use libc::c_int;
95/// snek! {
96///     Example {
97///         add: (x: c_int, y: c_int) -> c_int
98///     }
99/// }
100///
101/// fn main() {
102///     if let Ok(example) = Example::load("libexample.so") {
103///         println!("{}", unsafe { example.add(3, 7) })
104///     }
105/// }
106/// ```
107///
108/// Additional functions can be loaded by simply adding them in the macro usage:
109///
110/// ```
111/// # #[macro_use] extern crate snek;
112/// # extern crate libc;
113/// # use libc::c_int;
114/// snek! {
115///     Example {
116///         add: (x: c_int, y: c_int) -> c_int,
117///         hello: () -> ()
118///     }
119/// }
120/// # fn main () {}
121/// ```
122#[macro_export]
123macro_rules! snek {
124    ($sname:ident { 
125        $($symbol:ident : ($($pn: ident : $pt:ty),*) -> $ot:ty),*
126    }) => {
127        pub struct $sname<'a> {
128            handle: *mut libc::c_void,
129            $($symbol: snek::Symbol<'a>),*
130        }
131
132        impl<'a> $sname<'a> {
133            pub fn load<P>(path: P) -> Result<$sname<'a>, snek::Error> where P: AsRef<std::path::Path> {
134                let handle = match snek::load_library(path) {
135                    Ok(result) => result,
136                    Err(err) => return Err(err)
137                };
138
139                Ok($sname {
140                    handle: handle,
141                    $($symbol: match snek::load_symbol(handle, stringify!($symbol)) {
142                        Ok(result) => snek::Symbol::new(result),
143                        Err(err) => return Err(err)
144                    }),*
145                })
146            }
147
148            $(pub unsafe fn $symbol(&self, $($pn: $pt),*) -> $ot {
149                self.$symbol.with(|f: extern fn($($pt),*) -> $ot| f($($pn),*))
150            })*
151        }
152
153        impl<'a> Drop for $sname<'a> {
154            fn drop(&mut self) {
155                snek::drop_library(self.handle)
156            }
157        }
158    }
159}