use anyhow::Result;
use base64::{engine::general_purpose, Engine as _};
use std::fs;
use std::path::Path;
#[derive(Debug, Clone, Copy)]
pub enum ImageFormat {
Jpeg,
Png,
Gif,
WebP,
}
impl ImageFormat {
pub fn from_extension(ext: &str) -> Option<Self> {
match ext.to_lowercase().as_str() {
"jpg" | "jpeg" => Some(ImageFormat::Jpeg),
"png" => Some(ImageFormat::Png),
"gif" => Some(ImageFormat::Gif),
"webp" => Some(ImageFormat::WebP),
_ => None,
}
}
pub fn mime_type(&self) -> &'static str {
match self {
ImageFormat::Jpeg => "image/jpeg",
ImageFormat::Png => "image/png",
ImageFormat::Gif => "image/gif",
ImageFormat::WebP => "image/webp",
}
}
}
pub fn process_image_file(path: &Path) -> Result<String> {
if !path.exists() {
anyhow::bail!("Image file not found: {}", path.display());
}
let extension = path
.extension()
.and_then(|ext| ext.to_str())
.ok_or_else(|| anyhow::anyhow!("No file extension found"))?;
let format = ImageFormat::from_extension(extension)
.ok_or_else(|| anyhow::anyhow!("Unsupported image format: {}", extension))?;
let image_data = fs::read(path)?;
const MAX_SIZE: usize = 20 * 1024 * 1024; if image_data.len() > MAX_SIZE {
anyhow::bail!(
"Image file too large: {} bytes (max: {} bytes)",
image_data.len(),
MAX_SIZE
);
}
let base64_data = general_purpose::STANDARD.encode(&image_data);
let data_url = format!("data:{};base64,{}", format.mime_type(), base64_data);
Ok(data_url)
}
pub fn process_image_url(url: &str) -> Result<String> {
if !url.starts_with("http://") && !url.starts_with("https://") {
anyhow::bail!("Invalid image URL: must start with http:// or https://");
}
Ok(url.to_string())
}
pub fn process_images(paths: &[String]) -> Result<Vec<String>> {
let mut processed_images = Vec::new();
for path_str in paths {
let processed = if path_str.starts_with("http://") || path_str.starts_with("https://") {
process_image_url(path_str)?
} else {
let path = Path::new(path_str);
process_image_file(path)?
};
processed_images.push(processed);
}
Ok(processed_images)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_image_format_detection() {
assert!(matches!(
ImageFormat::from_extension("jpg"),
Some(ImageFormat::Jpeg)
));
assert!(matches!(
ImageFormat::from_extension("JPEG"),
Some(ImageFormat::Jpeg)
));
assert!(matches!(
ImageFormat::from_extension("png"),
Some(ImageFormat::Png)
));
assert!(matches!(
ImageFormat::from_extension("gif"),
Some(ImageFormat::Gif)
));
assert!(matches!(
ImageFormat::from_extension("webp"),
Some(ImageFormat::WebP)
));
assert!(ImageFormat::from_extension("txt").is_none());
}
#[test]
fn test_mime_types() {
assert_eq!(ImageFormat::Jpeg.mime_type(), "image/jpeg");
assert_eq!(ImageFormat::Png.mime_type(), "image/png");
assert_eq!(ImageFormat::Gif.mime_type(), "image/gif");
assert_eq!(ImageFormat::WebP.mime_type(), "image/webp");
}
}