use std::path::{Path, PathBuf};
fn library_extensions() -> &'static [&'static str] {
if cfg!(target_os = "macos") {
&["dylib"]
} else if cfg!(target_os = "linux") {
&["so"]
} else {
&["dll"]
}
}
pub fn has_library(dir: &Path, prefix: &str) -> bool {
let extensions = library_extensions();
dir.read_dir()
.ok()
.map(|entries| {
entries.filter_map(Result::ok).any(|entry| {
let name = entry.file_name();
let filename = name.to_string_lossy();
let matches_prefix = filename.starts_with(prefix);
let matches_extension = extensions
.iter()
.any(|ext| entry.path().extension().is_some_and(|e| e == *ext));
matches_prefix && matches_extension
})
})
.unwrap_or(false)
}
pub fn has_exact_library(dir: &Path, name: &str) -> bool {
let extensions = library_extensions();
let prefix = format!("lib{name}");
dir.read_dir()
.ok()
.map(|entries| {
entries.filter_map(Result::ok).any(|entry| {
let filename = entry.file_name();
let filename_str = filename.to_string_lossy();
let Some(rest) = filename_str.strip_prefix(&prefix) else {
return false;
};
extensions.iter().any(|ext| {
let suffix = format!(".{ext}");
rest == suffix
|| rest.starts_with(&format!("{suffix}."))
|| (rest.starts_with('.') && rest.ends_with(&suffix))
})
})
})
.unwrap_or(false)
}
pub fn find_library_in_common_paths(name: &str) -> Option<PathBuf> {
#[cfg(target_os = "macos")]
let common_paths = ["/opt/homebrew/lib", "/usr/local/lib", "/usr/lib"];
#[cfg(not(target_os = "macos"))]
let common_paths = ["/usr/local/lib", "/usr/lib", "/usr/lib64"];
for path in &common_paths {
let lib_path = Path::new(path);
if has_exact_library(lib_path, name) {
return Some(lib_path.to_path_buf());
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
fn create_temp_lib_dir(files: &[&str]) -> TempDir {
let temp_dir = TempDir::new().unwrap();
for file in files {
let path = temp_dir.path().join(file);
if file.ends_with('/') {
fs::create_dir_all(&path).unwrap();
} else {
fs::File::create(&path).unwrap();
}
}
temp_dir
}
#[test]
fn test_has_library_exact_match() {
#[cfg(target_os = "macos")]
let temp = create_temp_lib_dir(&["libkrun.dylib", "libkrun.1.dylib"]);
#[cfg(target_os = "linux")]
let temp = create_temp_lib_dir(&["libkrun.so", "libkrun.so.1"]);
#[cfg(target_os = "windows")]
let temp = create_temp_lib_dir(&["krun.dll"]);
assert!(has_library(temp.path(), "libkrun"));
}
#[test]
fn test_has_library_prefix_match() {
#[cfg(target_os = "macos")]
let temp = create_temp_lib_dir(&["libkrun.dylib", "libkrun-efi.dylib"]);
#[cfg(target_os = "linux")]
let temp = create_temp_lib_dir(&["libkrun.so", "libkrun-efi.so"]);
assert!(has_library(temp.path(), "libkrun"));
}
#[test]
fn test_has_library_no_match() {
let temp = create_temp_lib_dir(&["libother.dylib"]);
assert!(!has_library(temp.path(), "libkrun"));
}
#[test]
fn test_has_library_empty_dir() {
let temp = create_temp_lib_dir(&[]);
assert!(!has_library(temp.path(), "libkrun"));
}
#[test]
fn test_has_exact_library_unversioned() {
#[cfg(target_os = "macos")]
let temp = create_temp_lib_dir(&["libkrun.dylib", "libkrun-efi.dylib"]);
#[cfg(target_os = "linux")]
let temp = create_temp_lib_dir(&["libkrun.so", "libkrun-efi.so"]);
assert!(has_exact_library(temp.path(), "krun"));
assert!(has_exact_library(temp.path(), "krun-efi"));
}
#[test]
fn test_has_exact_library_versioned() {
#[cfg(target_os = "macos")]
let temp = create_temp_lib_dir(&["libkrun.dylib", "libkrun.1.dylib"]);
#[cfg(target_os = "linux")]
let temp = create_temp_lib_dir(&["libkrun.so.1", "libkrun.so.1.0"]);
assert!(has_exact_library(temp.path(), "krun"));
}
#[test]
fn test_has_exact_library_no_sibling_match() {
#[cfg(target_os = "macos")]
let temp = create_temp_lib_dir(&["libkrun-efi.dylib", "libkrun-efi.1.dylib"]);
#[cfg(target_os = "linux")]
let temp = create_temp_lib_dir(&["libkrun-efi.so", "libkrun-efi.so.1"]);
assert!(!has_exact_library(temp.path(), "krun"));
}
#[test]
fn test_has_exact_library_non_matching_extension() {
let temp = create_temp_lib_dir(&["libkrun.a", "libkrun.lib"]);
#[cfg(target_os = "macos")]
assert!(!has_exact_library(temp.path(), "krun"));
#[cfg(target_os = "linux")]
assert!(!has_exact_library(temp.path(), "krun"));
#[cfg(target_os = "windows")]
assert!(!has_exact_library(temp.path(), "krun"));
}
#[test]
fn test_has_exact_library_empty_dir() {
let temp = create_temp_lib_dir(&[]);
assert!(!has_exact_library(temp.path(), "krun"));
}
#[test]
#[cfg(target_os = "macos")]
fn test_has_exact_library_substring_prefix_issue() {
let temp = create_temp_lib_dir(&["libkrunner.dylib"]);
assert!(!has_exact_library(temp.path(), "krun"));
}
#[test]
fn test_find_library_in_common_paths() {
let result = find_library_in_common_paths("krun");
if let Some(path) = result {
assert!(path.exists());
}
}
#[test]
#[cfg(target_os = "macos")]
fn test_has_exact_library_with_version_suffix() {
let temp = create_temp_lib_dir(&["libkrun.1.dylib", "libkrun.2.dylib"]);
assert!(has_exact_library(temp.path(), "krun"));
}
#[test]
#[cfg(target_os = "linux")]
fn test_has_exact_library_linux_versioned() {
let temp = create_temp_lib_dir(&["libkrun.so.1.0", "libkrun.so.1"]);
assert!(has_exact_library(temp.path(), "krun"));
}
}