use std::borrow::Cow;
use std::ffi::{CStr, OsStr, OsString};
use std::fmt;
use std::mem::ManuallyDrop;
use std::sync::Arc;
use crate::os::{Lookup, imp};
use crate::{ArcSymbol, Error, Result, Symbol, SymbolName, SymbolType};
#[cfg_attr(
unix,
doc = "[`OpenOptions::no_delete`](crate::os::unix::OpenOptions::no_delete)."
)]
#[cfg_attr(not(unix), doc = "`os::unix::OpenOptions::no_delete`.")]
pub struct Library {
handle: ManuallyDrop<imp::Handle>,
}
impl Library {
#[cfg_attr(
unix,
doc = "[`os::unix::OpenOptions`](crate::os::unix::OpenOptions) for other flags."
)]
#[cfg_attr(not(unix), doc = "`os::unix::OpenOptions` for other flags.")]
pub unsafe fn open(path: impl AsRef<OsStr>) -> Result<Self> {
unsafe { imp::open(path.as_ref(), imp::DEFAULT_FLAGS) }.map(Self::from_handle)
}
pub unsafe fn open_first<I>(candidates: I) -> Result<Self>
where
I: IntoIterator,
I::Item: AsRef<OsStr>,
{
let mut last = None;
for candidate in candidates {
match unsafe { Self::open(candidate) } {
Ok(lib) => return Ok(lib),
Err(e) => last = Some(e),
}
}
Err(last.unwrap_or_else(|| Error::Open {
path: OsString::new(),
message: "no candidate library names given".into(),
}))
}
pub fn this() -> Result<Self> {
imp::this().map(Self::from_handle)
}
pub(crate) fn from_handle(handle: imp::Handle) -> Self {
Self {
handle: ManuallyDrop::new(handle),
}
}
pub(crate) fn handle(&self) -> &imp::Handle {
&self.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()?;
let lookup = imp::lookup(&self.handle, &name);
convert(name, lookup)
}
pub fn leak(self) -> &'static Library {
Box::leak(Box::new(self))
}
pub fn close(self) -> Result<()> {
let mut this = ManuallyDrop::new(self);
let handle = unsafe { ManuallyDrop::take(&mut this.handle) };
imp::close(handle)
}
}
pub(crate) fn convert<T: SymbolType>(name: Cow<'_, CStr>, lookup: Lookup) -> Result<T> {
match lookup {
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,
}),
}
}
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()
}
}
pub fn library_filename(name: impl AsRef<OsStr>) -> OsString {
let (prefix, suffix) = if cfg!(windows) {
("", ".dll")
} else if cfg!(target_vendor = "apple") {
("lib", ".dylib")
} else {
("lib", ".so")
};
let mut out = OsString::from(prefix);
out.push(name);
out.push(suffix);
out
}