use std::ffi::OsStr;
use crate::{Library, Result};
pub trait Bindings: Sized {
fn load(library: Library) -> Result<Self>;
fn library(&self) -> &Library;
unsafe fn open(path: impl AsRef<OsStr>) -> Result<Self> {
Self::load(unsafe { Library::open(path) }?)
}
unsafe fn open_first<I>(candidates: I) -> Result<Self>
where
I: IntoIterator,
I::Item: AsRef<OsStr>,
{
Self::load(unsafe { Library::open_first(candidates) }?)
}
}
#[macro_export]
macro_rules! library {
(
$(#[$meta:meta])*
$vis:vis struct $name:ident {
$(
unsafe extern $abi:literal {
$(
$(#[$($attr:tt)*])*
$ivis:vis $($kw:ident)+
$(( $($args:tt)* ))?
$(-> $ret:ty)?
$(: $sty:ty)?
;
)*
}
)*
}
) => {
$(#[$meta])*
$vis struct $name {
__dlsym_library: $crate::Library,
__dlsym_symbols: $crate::__private::Symbols<
{ <[&str]>::len(&[$($( ::core::stringify!($($kw)+), )*)*]) }
>,
}
impl $name {
const __DLSYM_NAMES: &'static [&'static str] = &[
$($( $crate::__dlsym_item!(@name {} $abi [$(#[$($attr)*])*] [$ivis] [$($kw)+]
[$(($($args)*))?] [$(-> $ret)?] [$(: $sty)?]), )*)*
];
const __DLSYM_ORDER: &'static [usize] = &$crate::__private::sort_order::<
{ <[&str]>::len(&[$($( ::core::stringify!($($kw)+), )*)*]) }
>(Self::__DLSYM_NAMES);
$($(
$crate::__dlsym_item!(@method {} $abi [$(#[$($attr)*])*] [$ivis] [$($kw)+]
[$(($($args)*))?] [$(-> $ret)?] [$(: $sty)?]);
)*)*
}
impl $crate::Bindings for $name {
fn load(library: $crate::Library) -> $crate::Result<Self> {
let symbols = [
$($( $crate::__dlsym_item!(@load {&library} $abi [$(#[$($attr)*])*] [$ivis]
[$($kw)+] [$(($($args)*))?] [$(-> $ret)?] [$(: $sty)?]), )*)*
];
::core::result::Result::Ok(Self {
__dlsym_symbols: $crate::__private::Symbols(symbols),
__dlsym_library: library,
})
}
fn library(&self) -> &$crate::Library {
&self.__dlsym_library
}
}
impl ::core::fmt::Debug for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct(::core::stringify!($name))
.field("library", &self.__dlsym_library)
.finish_non_exhaustive()
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __dlsym_item {
(@$mode:ident $ctx:tt $abi:literal [$($attrs:tt)*] $($rest:tt)*) => {
$crate::__dlsym_item!{@attrs ($mode $ctx $abi $($rest)*) [no] [] [] $($attrs)*}
};
(@attrs $k:tt [$o:ident] [$($l:tt)*] [$($a:tt)*] #[optional] $($t:tt)*) => {
$crate::__dlsym_item!{@attrs $k [yes] [$($l)*] [$($a)*] $($t)*}
};
(@attrs $k:tt [$o:ident] [$($l:tt)*] [$($a:tt)*] #[link_name = $link:literal] $($t:tt)*) => {
$crate::__dlsym_item!{@attrs $k [$o] [$link] [$($a)*] $($t)*}
};
(@attrs $k:tt [$o:ident] [$($l:tt)*] [$($a:tt)*] #[$($x:tt)*] $($t:tt)*) => {
$crate::__dlsym_item!{@attrs $k [$o] [$($l)*] [$($a)* #[$($x)*]] $($t)*}
};
(@attrs ($mode:ident $ctx:tt $abi:literal [$vis:vis] [$($kw:ident)+] $($rest:tt)*)
[$o:ident] [$($l:tt)*] [$($a:tt)*]) => {
$crate::__dlsym_item!{@decode ($mode $ctx $abi [$o] [$($l)*] [$($a)*] [$vis] $($rest)*)
$($kw)+}
};
(@decode ($($k:tt)*) fn $n:ident) => { $crate::__dlsym_item!{@gen $($k)* unsafe fn $n} };
(@decode ($($k:tt)*) unsafe fn $n:ident) => { $crate::__dlsym_item!{@gen $($k)* unsafe fn $n} };
(@decode ($($k:tt)*) safe fn $n:ident) => { $crate::__dlsym_item!{@gen $($k)* safe fn $n} };
(@decode ($($k:tt)*) static $n:ident) => { $crate::__dlsym_item!{@gen $($k)* unsafe static $n} };
(@decode ($($k:tt)*) unsafe static $n:ident) => { $crate::__dlsym_item!{@gen $($k)* unsafe static $n} };
(@decode ($($k:tt)*) safe static $n:ident) => { $crate::__dlsym_item!{@gen $($k)* safe static $n} };
(@decode ($($k:tt)*) static mut $n:ident) => { $crate::__dlsym_item!{@gen $($k)* unsafe mut $n} };
(@decode ($($k:tt)*) unsafe static mut $n:ident) => { $crate::__dlsym_item!{@gen $($k)* unsafe mut $n} };
(@decode ($($k:tt)*) $($kw:ident)+) => {
::core::compile_error!(::core::concat!(
"dlsym::library!: expected `[safe|unsafe] fn NAME(..)` or ",
"`[safe|unsafe] static [mut] NAME: TYPE`, found `",
::core::stringify!($($kw)+), "`"
));
};
(@gen name $ctx:tt $abi:literal $($rest:tt)*) => {
$crate::__dlsym_item!(@name_of $($rest)*)
};
(@name_of [$o:ident] [$($l:tt)*] [$($a:tt)*] [$vis:vis] $($x:tt)*) => {
$crate::__dlsym_item!(@last $($x)*)
};
(@last [$($args:tt)*] [$($ret:tt)*] [$($sty:tt)*] $safety:ident $kind:ident $n:ident) => {
::core::stringify!($n)
};
(@gen load {$lib:expr} $abi:literal [$o:ident] [$link:literal] $($rest:tt)*) => {
$crate::__private::resolve(
$lib,
const { $crate::__private::link_name(::core::concat!($link, "\0")) },
$crate::__dlsym_item!(@optional $o),
)?
};
(@gen load {$lib:expr} $abi:literal [$o:ident] [] [$($a:tt)*] [$vis:vis] $($x:tt)*) => {
$crate::__dlsym_item!(@load_default {$lib} [$o] $($x)*)
};
(@load_default {$lib:expr} [$o:ident] [$($args:tt)*] [$($ret:tt)*] [$($sty:tt)*]
$safety:ident $kind:ident $n:ident) => {
$crate::__private::resolve(
$lib,
const { $crate::__private::link_name(::core::concat!(::core::stringify!($n), "\0")) },
$crate::__dlsym_item!(@optional $o),
)?
};
(@optional yes) => { true };
(@optional no) => { false };
(@gen method $ctx:tt $abi:literal [$o:ident] [$($l:tt)*] [$($a:tt)*] [$vis:vis]
[($($arg:ident : $t:ty),+ , ...)] [$(-> $r:ty)?] [] $safety:ident fn $n:ident) => {
$($a)*
#[allow(non_snake_case)]
$vis fn $n(&self) -> $crate::__dlsym_item!(@wrap $o
$crate::Symbol<'_, unsafe extern $abi fn($($t),+ , ...) $(-> $r)?>)
{
let addr = self.__dlsym_symbols.0
[const { $crate::__private::index_of(Self::__DLSYM_NAMES, Self::__DLSYM_ORDER, ::core::stringify!($n)) }];
$crate::__dlsym_item!(@unwrap $o addr => $crate::__private::symbol(self, addr))
}
};
(@gen method $ctx:tt $abi:literal [$o:ident] [$($l:tt)*] [$($a:tt)*] [$vis:vis]
[($($arg:ident : $t:ty),* $(,)?)] [$(-> $r:ty)?] [] $safety:ident fn $n:ident) => {
$crate::__dlsym_item!{@fn_method $safety $o [$($a)*] [$vis] $n
[unsafe extern $abi fn($($t),*) $(-> $r)?] [$($arg : $t),*]
[$crate::__dlsym_item!(@ret $($r)?)]}
};
(@fn_method unsafe $o:ident [$($a:tt)*] [$vis:vis] $n:ident [$f:ty] [$($arg:ident : $t:ty),*] [$r:ty]) => {
$($a)*
#[allow(non_snake_case, clippy::too_many_arguments)]
$vis unsafe fn $n(&self, $($arg: $t),*) -> $crate::__dlsym_item!(@wrap $o $r) {
let addr = self.__dlsym_symbols.0
[const { $crate::__private::index_of(Self::__DLSYM_NAMES, Self::__DLSYM_ORDER, ::core::stringify!($n)) }];
$crate::__dlsym_item!(@unwrap $o addr =>
unsafe { ::core::mem::transmute::<*mut ::core::ffi::c_void, $f>(addr)($($arg),*) })
}
};
(@fn_method safe $o:ident [$($a:tt)*] [$vis:vis] $n:ident [$f:ty] [$($arg:ident : $t:ty),*] [$r:ty]) => {
$($a)*
#[allow(non_snake_case, clippy::too_many_arguments)]
$vis fn $n(&self, $($arg: $t),*) -> $crate::__dlsym_item!(@wrap $o $r) {
let addr = self.__dlsym_symbols.0
[const { $crate::__private::index_of(Self::__DLSYM_NAMES, Self::__DLSYM_ORDER, ::core::stringify!($n)) }];
$crate::__dlsym_item!(@unwrap $o addr =>
unsafe { ::core::mem::transmute::<*mut ::core::ffi::c_void, $f>(addr)($($arg),*) })
}
};
(@gen method $ctx:tt $abi:literal [$o:ident] [$($l:tt)*] [$($a:tt)*] [$vis:vis]
[] [] [: $t:ty] unsafe static $n:ident) => {
$($a)*
#[allow(non_snake_case)]
$vis unsafe fn $n(&self) -> $crate::__dlsym_item!(@wrap $o &$t) {
let addr = self.__dlsym_symbols.0
[const { $crate::__private::index_of(Self::__DLSYM_NAMES, Self::__DLSYM_ORDER, ::core::stringify!($n)) }];
$crate::__dlsym_item!(@unwrap $o addr => unsafe { &*addr.cast::<$t>() })
}
};
(@gen method $ctx:tt $abi:literal [$o:ident] [$($l:tt)*] [$($a:tt)*] [$vis:vis]
[] [] [: $t:ty] safe static $n:ident) => {
$($a)*
#[allow(non_snake_case)]
$vis fn $n(&self) -> $crate::__dlsym_item!(@wrap $o &$t) {
const { $crate::__private::assert_sync::<$t>() };
let addr = self.__dlsym_symbols.0
[const { $crate::__private::index_of(Self::__DLSYM_NAMES, Self::__DLSYM_ORDER, ::core::stringify!($n)) }];
$crate::__dlsym_item!(@unwrap $o addr => unsafe { &*addr.cast::<$t>() })
}
};
(@gen method $ctx:tt $abi:literal [$o:ident] [$($l:tt)*] [$($a:tt)*] [$vis:vis]
[] [] [: $t:ty] unsafe mut $n:ident) => {
$($a)*
#[allow(non_snake_case)]
$vis fn $n(&self) -> $crate::__dlsym_item!(@wrap $o *mut $t) {
let addr = self.__dlsym_symbols.0
[const { $crate::__private::index_of(Self::__DLSYM_NAMES, Self::__DLSYM_ORDER, ::core::stringify!($n)) }];
$crate::__dlsym_item!(@unwrap $o addr => addr.cast::<$t>())
}
};
(@gen method $ctx:tt $abi:literal [$o:ident] [$($l:tt)*] [$($a:tt)*] [$vis:vis]
[$($args:tt)*] [$($ret:tt)*] [$($sty:tt)*] $safety:ident $kind:ident $n:ident) => {
::core::compile_error!(::core::concat!(
"dlsym::library!: malformed item `", ::core::stringify!($n),
"`: functions need `(args)` and no `: TYPE`; statics need `: TYPE`"
));
};
(@ret) => { () };
(@ret $r:ty) => { $r };
(@wrap no $($t:tt)*) => { $($t)* };
(@wrap yes $($t:tt)*) => { ::core::option::Option<$($t)*> };
(@unwrap no $addr:ident => $e:expr) => { $e };
(@unwrap yes $addr:ident => $e:expr) => {
if $addr.is_null() { ::core::option::Option::None } else { ::core::option::Option::Some($e) }
};
}