use std::{ ffi::{CString, OsString}, time::SystemTime, }; use super::{canary, new, path, Library, Symbol}; use crate::{error, Result}; pub struct Module { handle: Option, loaded: SystemTime, path: OsString, } impl Module { pub fn from_name(name: &str) -> Result { Self::from_path(path::from_name(name)?) } pub fn from_path(path: OsString) -> Result { Ok(Self { handle: Some(new::from_path(&path)?), loaded: SystemTime::now(), path, }) } pub fn unload(&mut self) { canary::prepare(); self.close(); if !canary::check_and_reset() { let name = self.name().expect("Module is named"); error!("Module {name:?} is stuck and failed to unload."); } } pub(crate) fn close(&mut self) { if let Some(handle) = self.handle.take() { handle.close().expect("Module handle closed"); } } pub fn get(&self, name: &str) -> Result> { let cname = CString::new(name.to_owned()).expect("terminated string from provided name"); let handle = self .handle .as_ref() .expect("backing library loaded by this instance"); // SAFETY: Calls dlsym(3) on unix platforms. This might not have to be unsafe // if wrapped in libloading with_dlerror(). let sym = unsafe { handle.get::(cname.as_bytes()) }; let sym = sym.expect("symbol found; binding successful"); Ok(sym) } pub fn deleted(&self) -> Result { let mtime = path::mtime(self.path())?; let res = mtime > self.loaded; Ok(res) } pub fn name(&self) -> Result { path::to_name(self.path()) } #[must_use] pub fn path(&self) -> &OsString { &self.path } } impl Drop for Module { fn drop(&mut self) { if self.handle.is_some() { self.unload(); } } }