use crate::error::{GlintError, Result};
use std::path::{Path, PathBuf};
pub fn create_circular_kernel(radius: usize) -> ndarray::Array2<bool> {
let size = 2 * radius + 1;
let mut kernel = ndarray::Array2::from_elem((size, size), false);
let center = radius as i32;
for y in 0..size {
for x in 0..size {
let dy = y as i32 - center;
let dx = x as i32 - center;
let distance_sq = dx * dx + dy * dy;
if distance_sq <= (radius as i32) * (radius as i32) {
kernel[[y, x]] = true;
}
}
}
kernel
}
pub fn list_image_files(dir: &Path, extensions: &[String]) -> Result<Vec<PathBuf>> {
if !dir.exists() {
return Err(GlintError::Io(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("Directory does not exist: {}", dir.display()),
)));
}
if !dir.is_dir() {
return Err(GlintError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Path is not a directory: {}", dir.display()),
)));
}
let mut files = Vec::new();
let extensions_lower: Vec<String> = extensions.iter().map(|e| e.to_lowercase()).collect();
fn visit_dir(dir: &Path, extensions: &[String], files: &mut Vec<PathBuf>) -> Result<()> {
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
visit_dir(&path, extensions, files)?;
} else if path.is_file() {
if let Some(ext) = path.extension() {
let ext_str = ext.to_string_lossy().to_lowercase();
if extensions.iter().any(|e| *e == ext_str) {
files.push(path);
}
}
}
}
Ok(())
}
visit_dir(dir, &extensions_lower, &mut files)?;
files.sort();
Ok(files)
}
pub fn normalize_value(value: f64, bit_depth: u8) -> Result<f64> {
let max_value = match bit_depth {
8 => 255.0,
16 => 65535.0,
32 => 4294967295.0,
_ => return Err(GlintError::InvalidBitDepth { bit_depth }),
};
Ok(value / max_value)
}
pub fn max_value_for_bit_depth(bit_depth: u8) -> Result<f64> {
match bit_depth {
8 => Ok(255.0),
16 => Ok(65535.0),
32 => Ok(4294967295.0),
_ => Err(GlintError::InvalidBitDepth { bit_depth }),
}
}
pub fn validate_path_exists(path: &Path, path_type: &str) -> Result<()> {
if !path.exists() {
return Err(GlintError::Io(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("{} does not exist: {}", path_type, path.display()),
)));
}
Ok(())
}
pub fn validate_directory(path: &Path) -> Result<()> {
validate_path_exists(path, "Directory")?;
if !path.is_dir() {
return Err(GlintError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Path is not a directory: {}", path.display()),
)));
}
Ok(())
}
pub fn ensure_directory_exists(path: &Path) -> Result<()> {
if !path.exists() {
std::fs::create_dir_all(path)?;
} else if !path.is_dir() {
return Err(GlintError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Path exists but is not a directory: {}", path.display()),
)));
}
Ok(())
}
pub fn get_file_stem(path: &Path) -> Result<String> {
path.file_stem()
.and_then(|s| s.to_str())
.map(|s| s.to_string())
.ok_or_else(|| {
GlintError::processing(format!(
"Could not extract file stem from path: {}",
path.display()
))
})
}
pub fn has_extension(path: &Path, extensions: &[String]) -> bool {
if let Some(ext) = path.extension() {
let ext_str = ext.to_string_lossy().to_lowercase();
extensions.iter().any(|e| e.to_lowercase() == ext_str)
} else {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn test_create_circular_kernel() {
let kernel = create_circular_kernel(1);
assert_eq!(kernel.dim(), (3, 3));
assert!(kernel[[1, 1]]);
assert!(kernel[[0, 1]]);
assert!(kernel[[1, 0]]);
assert!(kernel[[2, 1]]);
assert!(kernel[[1, 2]]);
assert!(!kernel[[0, 0]]);
assert!(!kernel[[2, 2]]);
let kernel_0 = create_circular_kernel(0);
assert_eq!(kernel_0.dim(), (1, 1));
assert!(kernel_0[[0, 0]]);
}
#[test]
fn test_normalize_value() {
assert_eq!(normalize_value(255.0, 8).unwrap(), 1.0);
assert_eq!(normalize_value(127.5, 8).unwrap(), 0.5);
assert_eq!(normalize_value(0.0, 8).unwrap(), 0.0);
assert_eq!(normalize_value(65535.0, 16).unwrap(), 1.0);
assert_eq!(normalize_value(32767.5, 16).unwrap(), 0.5);
assert!(normalize_value(100.0, 7).is_err()); }
#[test]
fn test_max_value_for_bit_depth() {
assert_eq!(max_value_for_bit_depth(8).unwrap(), 255.0);
assert_eq!(max_value_for_bit_depth(16).unwrap(), 65535.0);
assert_eq!(max_value_for_bit_depth(32).unwrap(), 4294967295.0);
assert!(max_value_for_bit_depth(7).is_err());
}
#[test]
fn test_list_image_files() {
let temp_dir = tempdir().unwrap();
let dir_path = temp_dir.path();
fs::write(dir_path.join("image1.jpg"), b"fake image").unwrap();
fs::write(dir_path.join("image2.PNG"), b"fake image").unwrap();
fs::write(dir_path.join("document.txt"), b"text").unwrap();
let sub_dir = dir_path.join("subdir");
fs::create_dir(&sub_dir).unwrap();
fs::write(sub_dir.join("image3.tiff"), b"fake image").unwrap();
let extensions = vec!["jpg".to_string(), "png".to_string(), "tiff".to_string()];
let files = list_image_files(dir_path, &extensions).unwrap();
assert_eq!(files.len(), 3);
assert!(files.iter().any(|p| p.file_name().unwrap() == "image1.jpg"));
assert!(files.iter().any(|p| p.file_name().unwrap() == "image2.PNG"));
assert!(files
.iter()
.any(|p| p.file_name().unwrap() == "image3.tiff"));
assert!(!files
.iter()
.any(|p| p.file_name().unwrap() == "document.txt"));
}
#[test]
fn test_validate_directory() {
let temp_dir = tempdir().unwrap();
let dir_path = temp_dir.path();
let file_path = dir_path.join("file.txt");
fs::write(&file_path, b"content").unwrap();
assert!(validate_directory(dir_path).is_ok());
assert!(validate_directory(&file_path).is_err());
assert!(validate_directory(&dir_path.join("nonexistent")).is_err());
}
#[test]
fn test_ensure_directory_exists() {
let temp_dir = tempdir().unwrap();
let new_dir = temp_dir.path().join("new_directory");
assert!(!new_dir.exists());
ensure_directory_exists(&new_dir).unwrap();
assert!(new_dir.exists());
assert!(new_dir.is_dir());
ensure_directory_exists(&new_dir).unwrap();
}
#[test]
fn test_get_file_stem() {
let path = Path::new("image.jpg");
assert_eq!(get_file_stem(path).unwrap(), "image");
let path = Path::new("/path/to/image.with.dots.png");
assert_eq!(get_file_stem(path).unwrap(), "image.with.dots");
let path = Path::new("no_extension");
assert_eq!(get_file_stem(path).unwrap(), "no_extension");
}
#[test]
fn test_has_extension() {
let extensions = vec!["jpg".to_string(), "png".to_string()];
assert!(has_extension(Path::new("image.jpg"), &extensions));
assert!(has_extension(Path::new("image.JPG"), &extensions)); assert!(has_extension(Path::new("image.PNG"), &extensions));
assert!(!has_extension(Path::new("image.tiff"), &extensions));
assert!(!has_extension(Path::new("no_extension"), &extensions));
}
}