#![allow(unused_variables)]
use std::collections::HashMap;
use std::ffi::CString;
use std::fmt;
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use std::time::SystemTime;
use libloading::{Library, Symbol};
use crate::diagnostics::Error;
#[cfg(target_os = "windows")]
const LIBRARY_EXTENSION: &str = "dll";
#[cfg(target_os = "macos")]
const LIBRARY_EXTENSION: &str = "dylib";
#[cfg(target_os = "linux")]
const LIBRARY_EXTENSION: &str = "so";
#[derive(Debug, Clone)]
pub enum LibraryError {
NotFound(PathBuf),
LoadFailed {
path: PathBuf,
reason: String,
},
SymbolNotFound {
library: String,
symbol: String,
},
AlreadyLoaded(String),
HasActiveReferences(String),
InvalidHandle(String),
PlatformError(String),
}
impl fmt::Display for LibraryError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LibraryError::NotFound(path) => {
write!(f, "Library not found: {}", path.display())
}
LibraryError::LoadFailed { path, reason } => {
write!(f, "Failed to load library '{}': {}", path.display(), reason)
}
LibraryError::SymbolNotFound { library, symbol } => {
write!(f, "Symbol '{symbol}' not found in library '{library}'")
}
LibraryError::AlreadyLoaded(name) => {
write!(f, "Library '{name}' is already loaded")
}
LibraryError::HasActiveReferences(name) => {
write!(f, "Library '{name}' has active references and cannot be unloaded")
}
LibraryError::InvalidHandle(name) => {
write!(f, "Invalid library handle for '{name}'")
}
LibraryError::PlatformError(msg) => {
write!(f, "Platform-specific error: {msg}")
}
}
}
}
impl std::error::Error for LibraryError {}
impl From<LibraryError> for Error {
fn from(lib_error: LibraryError) -> Self {
Error::runtime_error(lib_error.to_string(), None)
}
}
#[derive(Debug)]
pub struct LibraryHandle {
library: Arc<Library>,
name: String,
path: PathBuf,
loaded_at: SystemTime,
ref_count: Arc<RwLock<usize>>,
symbol_cache: RwLock<HashMap<String, usize>>, }
impl LibraryHandle {
fn new(library: Library, name: String, path: PathBuf) -> Self {
Self {
library: Arc::new(library),
name,
path,
loaded_at: SystemTime::now(),
ref_count: Arc::new(RwLock::new(1)),
symbol_cache: RwLock::new(HashMap::new()),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn loaded_at(&self) -> SystemTime {
self.loaded_at
}
pub fn ref_count(&self) -> usize {
*self.ref_count.read().unwrap()
}
pub fn add_ref(&self) {
let mut count = self.ref_count.write().unwrap();
*count += 1;
}
pub fn release_ref(&self) -> usize {
let mut count = self.ref_count.write().unwrap();
if *count > 0 {
*count -= 1;
}
*count
}
pub fn get_symbol<T>(&self, name: &str) -> std::result::Result<Symbol<'_, T>, LibraryError> {
{
let cache = self.symbol_cache.read().unwrap();
if let Some(&addr) = cache.get(name) {
unsafe {
return Ok(std::mem::transmute_copy(&addr));
}
}
}
let c_name = CString::new(name)
.map_err(|_| LibraryError::SymbolNotFound {
library: self.name.clone(),
symbol: name.to_string(),
})?;
let symbol: Symbol<'_, T> = unsafe {
self.library.get(c_name.as_bytes())
.map_err(|_e| LibraryError::SymbolNotFound {
library: self.name.clone(),
symbol: name.to_string(),
})?
};
{
let mut cache = self.symbol_cache.write().unwrap();
unsafe {
let addr: usize = std::mem::transmute_copy(&symbol);
cache.insert(name.to_string(), addr);
}
}
Ok(symbol)
}
pub fn has_symbol(&self, name: &str) -> bool {
{
let cache = self.symbol_cache.read().unwrap();
if cache.contains_key(name) {
return true;
}
}
let c_name = match CString::new(name) {
Ok(name) => name,
Err(_) => return false,
};
unsafe {
self.library.get::<*const ()>(c_name.as_bytes()).is_ok()
}
}
pub fn list_symbols(&self) -> Vec<String> {
vec![]
}
}
impl Clone for LibraryHandle {
fn clone(&self) -> Self {
self.add_ref();
Self {
library: Arc::clone(&self.library),
name: self.name.clone(),
path: self.path.clone(),
loaded_at: self.loaded_at,
ref_count: Arc::clone(&self.ref_count),
symbol_cache: RwLock::new(HashMap::new()), }
}
}
impl Drop for LibraryHandle {
fn drop(&mut self) {
self.release_ref();
}
}
#[derive(Debug, Clone)]
pub struct LibrarySearchConfig {
pub search_paths: Vec<PathBuf>,
pub use_system_paths: bool,
pub use_current_dir: bool,
pub prefixes: Vec<String>,
}
impl Default for LibrarySearchConfig {
fn default() -> Self {
Self {
search_paths: vec![],
use_system_paths: true,
use_current_dir: true,
prefixes: vec!["lib".to_string(), "".to_string()],
}
}
}
#[derive(Debug)]
pub struct LibraryManager {
libraries: RwLock<HashMap<String, LibraryHandle>>,
search_config: RwLock<LibrarySearchConfig>,
stats: RwLock<LibraryStats>,
dependencies: RwLock<HashMap<String, Vec<String>>>,
}
#[derive(Debug, Default, Clone)]
pub struct LibraryStats {
pub total_loaded: usize,
pub currently_loaded: usize,
pub symbol_lookups: u64,
pub successful_lookups: u64,
pub failed_lookups: u64,
}
impl Default for LibraryManager {
fn default() -> Self {
Self::new()
}
}
impl LibraryManager {
pub fn new() -> Self {
Self {
libraries: RwLock::new(HashMap::new()),
search_config: RwLock::new(LibrarySearchConfig::default()),
stats: RwLock::new(LibraryStats::default()),
dependencies: RwLock::new(HashMap::new()),
}
}
pub fn set_search_config(&self, config: LibrarySearchConfig) {
let mut search_config = self.search_config.write().unwrap();
*search_config = config;
}
pub fn add_search_path<P: AsRef<Path>>(&self, path: P) {
let mut config = self.search_config.write().unwrap();
config.search_paths.push(path.as_ref().to_path_buf());
}
fn find_library(&self, name: &str) -> Option<PathBuf> {
let config = self.search_config.read().unwrap();
let extensions = vec![LIBRARY_EXTENSION];
let prefixes = &config.prefixes;
let mut candidates = Vec::new();
for prefix in prefixes {
for ext in &extensions {
let filename = if prefix.is_empty() {
format!("{name}.{ext}")
} else {
format!("{prefix}{name}.{ext}")
};
candidates.push(filename);
}
}
candidates.push(name.to_string());
let mut search_paths = Vec::new();
if config.use_current_dir {
search_paths.push(PathBuf::from("."));
}
search_paths.extend(config.search_paths.iter().cloned());
if config.use_system_paths {
#[cfg(unix)]
{
search_paths.extend(vec![
PathBuf::from("/usr/lib"),
PathBuf::from("/usr/local/lib"),
PathBuf::from("/lib"),
]);
}
#[cfg(windows)]
{
if let Ok(sys_dir) = std::env::var("SYSTEMROOT") {
search_paths.push(PathBuf::from(sys_dir).join("System32"));
}
}
}
for path in &search_paths {
for candidate in &candidates {
let full_path = path.join(candidate);
if full_path.exists() {
return Some(full_path);
}
}
}
None
}
pub fn load_library(&self, name: &str) -> std::result::Result<LibraryHandle, LibraryError> {
{
let libraries = self.libraries.read().unwrap();
if let Some(handle) = libraries.get(name) {
return Ok(handle.clone());
}
}
let library_path = if Path::new(name).is_absolute() || name.contains('/') || name.contains('\\') {
PathBuf::from(name)
} else {
self.find_library(name).ok_or_else(|| LibraryError::NotFound(PathBuf::from(name)))?
};
let library = unsafe {
Library::new(&library_path)
.map_err(|e| LibraryError::LoadFailed {
path: library_path.clone(),
reason: e.to_string(),
})?
};
let handle = LibraryHandle::new(library, name.to_string(), library_path);
{
let mut libraries = self.libraries.write().unwrap();
libraries.insert(name.to_string(), handle.clone());
}
{
let mut stats = self.stats.write().unwrap();
stats.total_loaded += 1;
stats.currently_loaded = self.libraries.read().unwrap().len();
}
Ok(handle)
}
pub fn unload_library(&self, name: &str) -> std::result::Result<(), LibraryError> {
let handle = {
let mut libraries = self.libraries.write().unwrap();
libraries.remove(name)
};
if let Some(handle) = handle {
if handle.ref_count() > 1 {
return Err(LibraryError::HasActiveReferences(name.to_string()));
}
let mut stats = self.stats.write().unwrap();
stats.currently_loaded = self.libraries.read().unwrap().len();
Ok(())
} else {
Err(LibraryError::NotFound(PathBuf::from(name)))
}
}
pub fn get_library(&self, name: &str) -> Option<LibraryHandle> {
let libraries = self.libraries.read().unwrap();
libraries.get(name).cloned()
}
pub fn list_libraries(&self) -> Vec<String> {
let libraries = self.libraries.read().unwrap();
libraries.keys().cloned().collect()
}
pub fn stats(&self) -> LibraryStats {
self.stats.read().unwrap().clone()
}
pub fn unload_all(&self) -> std::result::Result<(), Vec<LibraryError>> {
let names: Vec<String> = {
let libraries = self.libraries.read().unwrap();
libraries.keys().cloned().collect()
};
let mut errors = Vec::new();
for name in names {
if let Err(e) = self.unload_library(&name) {
errors.push(e);
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn add_dependency(&self, dependent: &str, dependency: &str) {
let mut deps = self.dependencies.write().unwrap();
deps.entry(dependent.to_string())
.or_default()
.push(dependency.to_string());
}
pub fn get_dependencies(&self, library: &str) -> Vec<String> {
let deps = self.dependencies.read().unwrap();
deps.get(library).cloned().unwrap_or_default()
}
}
impl LibraryManager {
pub fn load_symbol<T>(&self, library_name: &str, symbol_name: &str)
-> std::result::Result<*const T, LibraryError> {
let handle = self.load_library(library_name)?;
let symbol = handle.get_symbol::<T>(symbol_name)?;
Ok(unsafe { std::mem::transmute::<libloading::os::unix::Symbol<T>, *const T>(symbol.into_raw()) })
}
pub fn get_symbol<T>(&self, library_name: &str, symbol_name: &str)
-> std::result::Result<*const T, LibraryError> {
let handle = self.load_library(library_name)?;
{
let mut stats = self.stats.write().unwrap();
stats.symbol_lookups += 1;
}
match handle.get_symbol::<T>(symbol_name) {
Ok(symbol) => {
let mut stats = self.stats.write().unwrap();
stats.successful_lookups += 1;
Ok(unsafe { std::mem::transmute::<libloading::os::unix::Symbol<T>, *const T>(symbol.into_raw()) })
}
Err(e) => {
let mut stats = self.stats.write().unwrap();
stats.failed_lookups += 1;
Err(LibraryError::SymbolNotFound {
library: library_name.to_string(),
symbol: symbol_name.to_string(),
})
}
}
}
}
lazy_static::lazy_static! {
pub static ref GLOBAL_LIBRARY_MANAGER: LibraryManager = LibraryManager::new();
}
pub fn load_library(name: &str) -> std::result::Result<LibraryHandle, LibraryError> {
GLOBAL_LIBRARY_MANAGER.load_library(name)
}
pub fn unload_library(name: &str) -> std::result::Result<(), LibraryError> {
GLOBAL_LIBRARY_MANAGER.unload_library(name)
}
pub fn get_library(name: &str) -> Option<LibraryHandle> {
GLOBAL_LIBRARY_MANAGER.get_library(name)
}
pub fn load_symbol<T>(library_name: &str, symbol_name: &str)
-> std::result::Result<*const T, LibraryError> {
GLOBAL_LIBRARY_MANAGER.load_symbol(library_name, symbol_name)
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
#[test]
fn test_library_search_config() {
let config = LibrarySearchConfig::default();
assert!(config.use_system_paths);
assert!(config.use_current_dir);
assert!(!config.prefixes.is_empty());
}
#[test]
fn test_library_manager_creation() {
let manager = LibraryManager::new();
let stats = manager.stats();
assert_eq!(stats.currently_loaded, 0);
assert_eq!(stats.total_loaded, 0);
}
#[test]
fn test_library_path_search() {
let manager = LibraryManager::new();
let result = manager.find_library("nonexistent_library_12345");
assert!(result.is_none());
}
#[test]
fn test_search_path_addition() {
let manager = LibraryManager::new();
manager.add_search_path("/custom/path");
let config = manager.search_config.read().unwrap();
assert!(config.search_paths.contains(&PathBuf::from("/custom/path")));
}
}