pub mod completions;
pub mod compress;
pub mod convert;
pub mod detect;
pub mod favicon;
pub mod info;
pub mod install;
pub mod remove_watermark;
pub mod split;
pub mod style;
pub mod transparent;
use anyhow::Result;
use std::path::{Path, PathBuf};
const IMAGE_EXTS: &[&str] = &["jpg", "jpeg", "png", "webp", "bmp", "gif", "tiff", "tif"];
pub fn collect_inputs(path: &Path, recursive: bool) -> Result<Vec<PathBuf>> {
if path.is_file() {
return Ok(vec![path.to_path_buf()]);
}
if !path.is_dir() {
anyhow::bail!("Input not found: {}", path.display());
}
let mut files = Vec::new();
walk(path, recursive, &mut files)?;
files.sort();
Ok(files)
}
fn walk(dir: &Path, recursive: bool, out: &mut Vec<PathBuf>) -> Result<()> {
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let p = entry.path();
if p.is_dir() {
if recursive {
walk(&p, true, out)?;
}
} else if p.is_file()
&& let Some(ext) = p.extension().and_then(|e| e.to_str())
&& IMAGE_EXTS.contains(&ext.to_lowercase().as_str())
{
out.push(p);
}
}
Ok(())
}
pub fn mirror_path(input: &Path, input_root: &Path, output_root: Option<&Path>) -> PathBuf {
match output_root {
Some(root) => {
let rel = input.strip_prefix(input_root).unwrap_or(input);
root.join(rel)
}
None => input.to_path_buf(),
}
}
pub fn ensure_parent(path: &Path) -> Result<()> {
if let Some(parent) = path.parent()
&& !parent.as_os_str().is_empty()
{
std::fs::create_dir_all(parent)?;
}
Ok(())
}
pub fn format_size(bytes: u64) -> String {
if bytes < 1024 {
format!("{bytes} B")
} else if bytes < 1024 * 1024 {
format!("{:.1} KB", bytes as f64 / 1024.0)
} else if bytes < 1024 * 1024 * 1024 {
format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0))
} else {
format!("{:.2} GB", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
}
}