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}