use std::ffi::OsStr;
use std::fmt;
use std::mem::ManuallyDrop;
use std::sync::Arc;
use crate::os::{Lookup, imp};
use crate::{ArcSymbol, Error, Result, Symbol, SymbolName, SymbolType};
pub struct Library {
handle: ManuallyDrop<imp::Handle>,
}
impl Library {
#[cfg_attr(
unix,
doc = "[`os::unix::LibraryExt`](crate::os::unix::LibraryExt) for other flags."
)]
#[cfg_attr(not(unix), doc = "`os::unix::LibraryExt` for other flags.")]
pub unsafe fn open(path: impl AsRef<OsStr>) -> Result<Self> {
#[cfg(unix)]
let handle = unsafe {
imp::open(
Some(path.as_ref()),
imp::flags::RTLD_NOW | imp::flags::RTLD_LOCAL,
)
}?;
#[cfg(windows)]
let handle = unsafe { imp::open(path.as_ref()) }?;
Ok(Self::from_handle(handle))
}
pub(crate) fn from_handle(handle: imp::Handle) -> Self {
Self {
handle: ManuallyDrop::new(handle),
}
}
pub unsafe fn get<T: SymbolType>(&self, name: impl SymbolName) -> Result<Symbol<'_, T>> {
self.resolve(name).map(Symbol::new)
}
pub unsafe fn get_arc<T: SymbolType>(
self: &Arc<Self>,
name: impl SymbolName,
) -> Result<ArcSymbol<T>> {
self.resolve(name)
.map(|inner| ArcSymbol::new(inner, Arc::clone(self)))
}
fn resolve<T: SymbolType>(&self, name: impl SymbolName) -> Result<T> {
let name = name.to_symbol_name()?;
match imp::lookup(&self.handle, &name) {
Lookup::Found(addr) => T::from_address(addr).ok_or_else(|| Error::NullSymbol {
name: name.into_owned(),
}),
Lookup::NotFound(message) => Err(Error::SymbolNotFound {
name: name.into_owned(),
message,
}),
}
}
pub fn close(self) -> Result<()> {
let mut this = ManuallyDrop::new(self);
let handle = unsafe { ManuallyDrop::take(&mut this.handle) };
imp::close(handle)
}
}
impl Drop for Library {
fn drop(&mut self) {
let handle = unsafe { ManuallyDrop::take(&mut self.handle) };
let _ = imp::close(handle);
}
}
impl fmt::Debug for Library {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Library")
.field("handle", &*self.handle)
.finish()
}
}