rsfindlibs 0.1.1

Find shared libraries in the system and provide a useful macro to load them.
Documentation
pub mod  macros;

use std::env;
use std::path::PathBuf;
use log::{debug};

pub fn find(lib_name: &str, pkg_name: Option<&str>) -> Option<String> {
    // Determine the package name
    debug!("Starting search for library '{}' package = '{}'", lib_name, pkg_name.unwrap_or("None"));
    let pkg_name = pkg_name.unwrap_or(lib_name);

    // Determine the library file extension based on the platform
    let extension = match env::consts::OS {
        "windows" => ".dll",
        "macos" => ".dylib",
        _ => ".so", // Assume Unix-like system
    };
    debug!("Chose library extension '{}' for OS '{}'", extension, env::consts::OS);

    // sys.prefix equivalent: `env::current_exe()` gives the path to the executable
    let mut roots = vec![env::current_exe().ok()?.parent()?.to_path_buf()];
    debug!("Adding these current executable directories to search roots:");
    for root in &roots {
        debug!("  {}", root.to_string_lossy());
    }

    // Check for `CONDA_PREFIX` in the environment
    if let Ok(conda_prefix) = env::var("CONDA_PREFIX") {
        debug!("Detected Conda environment:");
        debug!("  {}", conda_prefix.to_string());
        roots.push(PathBuf::from(conda_prefix));
    }



    // Check environment variables like `PKG_HOME` and `PKG_DIR`
    let env_suffixes = ["HOME", "DIR"];


    for suffix in &env_suffixes {
        let combined = format!("{}_{}", pkg_name, suffix);
        for env_var in [combined.to_uppercase(), combined.to_lowercase()].iter() {
            if let Ok(home) = env::var(&env_var) {
                if home.is_empty() {
                    continue;
                }
                debug!("Found environment variable '{}'.", env_var);
                let fullname = PathBuf::from(&env_var);
                debug!("  {}", fullname.display());
                roots.push(fullname);
            }
        }
    }
    

    // Check `LD_LIBRARY_PATH`, `DYLD_LIBRARY_PATH`
    for path_var in &["LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH"] {
        if let Ok(paths) = env::var(path_var) {
            if paths.is_empty() {
                continue;
            }
            debug!("Found {}", path_var);
            for home in paths.split(':') {
                let fullname = PathBuf::from(home).join(format!("lib{}{}", lib_name, extension));
                debug!("  {}", fullname.display());
                roots.push(fullname);
            }
        }
    }

    // Check common system paths
    debug!("Checking common system paths:");
    for root in &["/", "/usr/", "/usr/local/", "/opt/", "/opt/homebrew/"] {
        let folder = PathBuf::from(root);
        if !folder.exists() {
            continue;
        }
        debug!("   {}", folder.display());
        roots.push(folder);
    }

    // Search in `lib` and `lib64` under roots
    debug!("Searching roots in order:");
    let mut found : Vec<String> = vec![];
    for root in &roots {
        for lib_dir in &["lib", "lib64"] {
            let fullname = root.join(lib_dir).join(format!("lib{}{}", lib_name, extension));
            debug!("  {}  {}", if fullname.exists() { "" } else { "" }, fullname.to_string_lossy());
            if fullname.exists() {
                found.push(fullname.to_string_lossy().to_string());
            }
        }
    }

    if found.len() > 0 {
        return Some(found[0].clone());
    }

    // Fallback: Use `find_library` (requires the `cc` crate for better functionality)
    let cc_attempt = find_library(lib_name);
    if cc_attempt.is_some() {
        debug!("Found library using `cc` crate: {}", cc_attempt.as_ref().unwrap());
    }
    return cc_attempt
}

fn find_library(lib_name: &str) -> Option<String> {
    if let Ok(output) = std::process::Command::new("ldconfig").arg("-p").output() {
        let output_str = String::from_utf8_lossy(&output.stdout);
        for line in output_str.lines() {
            if line.contains(&format!("lib{}.", lib_name)) {
                if let Some(path) = line.split_whitespace().last() {
                    return Some(path.to_string());
                }
            }
        }
    }
    None
}