Add ImageLoader and file discovery modules
ImageLoader: load image info (dimensions, format, file size) and pixels. Discovery: find image files by extension, flat or recursive, single file or directory. All 9 tests passing.
This commit is contained in:
57
pixstrip-core/src/discovery.rs
Normal file
57
pixstrip-core/src/discovery.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
const IMAGE_EXTENSIONS: &[&str] = &[
|
||||
"jpg", "jpeg", "png", "webp", "avif", "gif", "tiff", "tif", "bmp", "heic", "heif", "jxl",
|
||||
"svg", "ico",
|
||||
];
|
||||
|
||||
fn is_image_extension(ext: &str) -> bool {
|
||||
IMAGE_EXTENSIONS.contains(&ext.to_lowercase().as_str())
|
||||
}
|
||||
|
||||
pub fn discover_images(path: &Path, recursive: bool) -> Vec<PathBuf> {
|
||||
if path.is_file() {
|
||||
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
|
||||
if is_image_extension(ext) {
|
||||
return vec![path.to_path_buf()];
|
||||
}
|
||||
}
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
if !path.is_dir() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let max_depth = if recursive { usize::MAX } else { 1 };
|
||||
|
||||
WalkDir::new(path)
|
||||
.max_depth(max_depth)
|
||||
.into_iter()
|
||||
.filter_map(|entry| entry.ok())
|
||||
.filter(|entry| entry.file_type().is_file())
|
||||
.filter(|entry| {
|
||||
entry
|
||||
.path()
|
||||
.extension()
|
||||
.and_then(|ext| ext.to_str())
|
||||
.is_some_and(is_image_extension)
|
||||
})
|
||||
.map(|entry| entry.into_path())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn has_subdirectories(path: &Path) -> bool {
|
||||
if !path.is_dir() {
|
||||
return false;
|
||||
}
|
||||
std::fs::read_dir(path)
|
||||
.map(|entries| {
|
||||
entries
|
||||
.filter_map(|e| e.ok())
|
||||
.any(|e| e.file_type().is_ok_and(|ft| ft.is_dir()))
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
pub mod config;
|
||||
pub mod discovery;
|
||||
pub mod error;
|
||||
pub mod loader;
|
||||
pub mod operations;
|
||||
pub mod pipeline;
|
||||
pub mod preset;
|
||||
|
||||
69
pixstrip-core/src/loader.rs
Normal file
69
pixstrip-core/src/loader.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::error::{PixstripError, Result};
|
||||
use crate::types::{Dimensions, ImageFormat};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImageInfo {
|
||||
pub dimensions: Dimensions,
|
||||
pub format: Option<ImageFormat>,
|
||||
pub file_size: u64,
|
||||
}
|
||||
|
||||
pub struct ImageLoader;
|
||||
|
||||
impl ImageLoader {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
pub fn load_info(&self, path: &Path) -> Result<ImageInfo> {
|
||||
let metadata = std::fs::metadata(path).map_err(|e| PixstripError::ImageLoad {
|
||||
path: path.to_path_buf(),
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let reader =
|
||||
image::ImageReader::open(path).map_err(|e| PixstripError::ImageLoad {
|
||||
path: path.to_path_buf(),
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let reader = reader.with_guessed_format().map_err(|e| PixstripError::ImageLoad {
|
||||
path: path.to_path_buf(),
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let img = reader.decode().map_err(|e| PixstripError::ImageLoad {
|
||||
path: path.to_path_buf(),
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let format = path
|
||||
.extension()
|
||||
.and_then(|ext| ext.to_str())
|
||||
.and_then(ImageFormat::from_extension);
|
||||
|
||||
Ok(ImageInfo {
|
||||
dimensions: Dimensions {
|
||||
width: img.width(),
|
||||
height: img.height(),
|
||||
},
|
||||
format,
|
||||
file_size: metadata.len(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load_pixels(&self, path: &Path) -> Result<image::DynamicImage> {
|
||||
image::open(path).map_err(|e| PixstripError::ImageLoad {
|
||||
path: path.to_path_buf(),
|
||||
reason: e.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ImageLoader {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user