use crate::DotmaxError;
use image::DynamicImage;
use std::path::Path;
use tracing::{debug, info};
pub const MAX_IMAGE_WIDTH: u32 = 10_000;
pub const MAX_IMAGE_HEIGHT: u32 = 10_000;
pub fn load_from_path(path: &Path) -> Result<DynamicImage, DotmaxError> {
info!("Loading image from {:?}", path);
std::fs::metadata(path).map_err(|e| DotmaxError::ImageLoad {
path: path.to_path_buf(),
source: image::ImageError::IoError(e),
})?;
let img = image::open(path).map_err(|e| DotmaxError::ImageLoad {
path: path.to_path_buf(),
source: e,
})?;
debug!("Image dimensions: {}×{}", img.width(), img.height());
if img.width() > MAX_IMAGE_WIDTH || img.height() > MAX_IMAGE_HEIGHT {
return Err(DotmaxError::InvalidImageDimensions {
width: img.width(),
height: img.height(),
});
}
Ok(img)
}
pub fn load_from_bytes(bytes: &[u8]) -> Result<DynamicImage, DotmaxError> {
info!("Loading image from byte buffer ({} bytes)", bytes.len());
let img = image::load_from_memory(bytes).map_err(|e| DotmaxError::ImageLoad {
path: std::path::PathBuf::from("<bytes>"),
source: e,
})?;
debug!("Image dimensions: {}×{}", img.width(), img.height());
if img.width() > MAX_IMAGE_WIDTH || img.height() > MAX_IMAGE_HEIGHT {
return Err(DotmaxError::InvalidImageDimensions {
width: img.width(),
height: img.height(),
});
}
Ok(img)
}
#[must_use]
pub fn supported_formats() -> Vec<&'static str> {
vec!["png", "jpg", "jpeg", "gif", "bmp", "webp", "tiff"]
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn test_supported_formats_returns_expected_list() {
let formats = supported_formats();
assert_eq!(formats.len(), 7);
assert!(formats.contains(&"png"));
assert!(formats.contains(&"jpg"));
assert!(formats.contains(&"jpeg"));
assert!(formats.contains(&"gif"));
assert!(formats.contains(&"bmp"));
assert!(formats.contains(&"webp"));
assert!(formats.contains(&"tiff"));
}
#[test]
fn test_max_dimensions_constants_are_sensible() {
assert_eq!(MAX_IMAGE_WIDTH, 10_000);
assert_eq!(MAX_IMAGE_HEIGHT, 10_000);
}
#[test]
fn test_load_from_path_with_valid_png() {
let path = Path::new("tests/fixtures/images/sample.png");
let result = load_from_path(path);
assert!(
result.is_ok(),
"Failed to load sample.png: {:?}",
result.err()
);
let img = result.unwrap();
assert!(img.width() > 0 && img.width() <= 100);
assert!(img.height() > 0 && img.height() <= 100);
}
#[test]
fn test_load_from_path_with_missing_file() {
let path = Path::new("tests/fixtures/images/nonexistent.png");
let result = load_from_path(path);
assert!(result.is_err());
match result.unwrap_err() {
DotmaxError::ImageLoad { path: err_path, .. } => {
assert_eq!(
err_path,
PathBuf::from("tests/fixtures/images/nonexistent.png")
);
}
other => panic!("Expected ImageLoad error, got {:?}", other),
}
}
#[test]
fn test_load_from_path_with_corrupted_file() {
let path = Path::new("tests/fixtures/images/corrupted.png");
let result = load_from_path(path);
assert!(result.is_err(), "Should fail on corrupted PNG");
match result.unwrap_err() {
DotmaxError::ImageLoad { .. } => {
}
other => panic!(
"Expected ImageLoad error for corrupted file, got {:?}",
other
),
}
}
#[test]
fn test_load_from_bytes_with_valid_png_bytes() {
let path = Path::new("tests/fixtures/images/sample.png");
let bytes = std::fs::read(path).expect("Failed to read sample.png");
let result = load_from_bytes(&bytes);
assert!(
result.is_ok(),
"Failed to load from bytes: {:?}",
result.err()
);
let img = result.unwrap();
assert!(img.width() > 0);
assert!(img.height() > 0);
}
#[test]
fn test_load_from_bytes_with_invalid_bytes() {
let invalid_bytes = b"This is not an image!";
let result = load_from_bytes(invalid_bytes);
assert!(result.is_err(), "Should fail on invalid bytes");
match result.unwrap_err() {
DotmaxError::ImageLoad { .. } => {
}
other => panic!("Expected ImageLoad error, got {:?}", other),
}
}
#[test]
fn test_dimension_validation_rejects_oversized_width() {
assert_eq!(MAX_IMAGE_WIDTH, 10_000);
assert_eq!(MAX_IMAGE_HEIGHT, 10_000);
}
#[test]
fn test_load_from_path_validates_path_exists() {
let path = Path::new("/nonexistent/directory/image.png");
let result = load_from_path(path);
assert!(result.is_err());
matches!(result.unwrap_err(), DotmaxError::ImageLoad { .. });
}
}