#![warn(unused_crate_dependencies)] use clap::Parser; use magick_rust::MagickWand; use std::{fs, path::{Path, PathBuf}}; use rand::seq::IteratorRandom; use xdg::BaseDirectories; #[derive(Parser)] #[command(version, about, long_about = None)] struct Cli { /// Which folder to use to locate the images #[arg(short, long, value_name = "DIRECTORY")] directory: PathBuf, /// Size of the image to be output #[arg(short, long, value_name = "WIDTH")] size: usize, /// Search directory recursively #[arg(short = 'r', long = "recursive", action = clap::ArgAction::SetTrue)] recursive: Option, } pub struct ImgOps { width: usize, height: usize, x_offset: isize, y_offset: isize } pub fn get_random_image(directory: &Path,recursive: Option) -> Result { match directory.canonicalize() { Ok(d) => match d.is_dir() { true => { let mut rng = rand::rng(); let walker = globwalk::GlobWalkerBuilder::from_patterns(&d, &["*.{png,jpg,gif,webp,heic}"]) .case_insensitive(true); let imgs = match recursive.is_some_and(|x|x) { true => { walker.build().unwrap() }, false => { walker.max_depth(1).build().unwrap() }, }; match imgs.choose(&mut rng).unwrap() { Ok(o) => Ok(PathBuf::from(o.path())), Err(e) => Err(e.to_string()), } }, false => Err(format!("{} is not a directory",d.to_str().unwrap_or_default())), }, Err(e) => Err(e.to_string()), } } pub fn output_path(image_path: PathBuf) -> Result { let xdg_dirs = BaseDirectories::with_prefix("random-image-scaler"); //; match xdg_dirs.get_cache_home() { Some(cache) => match xdg_dirs.create_cache_directory(cache) { Ok(mut cache) => { cache.push(image_path.strip_prefix("/").unwrap()); cache.add_extension("jpg"); Ok(cache) }, Err(e) => Err(e.to_string()), } None => Err("Could not resolve cache home?".to_string()), } } pub fn calc_img_ops(orig_height: usize, orig_width: usize, size: usize) -> ImgOps { let r = orig_width as f32 / orig_height as f32; //println!("r: {:#?}",r); let w = match r.gt(&1.0) { true => (r.abs() * size as f32) as usize, false => size, }; //println!("w: {:#?}",w); let h = match r.lt(&1.0) { true => (size as f32 / r) as usize, false => size, }; //println!("h: {:#?}",h); let x = match r.gt(&1.0) { true => (w - size) / 2, false => 0_usize, } as isize; //println!("x: {:#?}",x); let y = match r.lt(&1.0) { true => (h - size) / 2, false => 0_usize, } as isize; //println!("y: {:#?}",y); ImgOps{ width: w, height: h, x_offset: x, y_offset: y } } pub fn scale_image(image_path: PathBuf, cached_image_path: PathBuf, size: usize) -> Result { let magick_image = MagickWand::new(); match image_path.to_str() { Some(img_str) => { match magick_image.read_image(img_str) { Ok(_) => { let ops = calc_img_ops(magick_image.get_image_height(), magick_image.get_image_width(), size); match magick_image.resize_image(ops.width, ops.height, magick_rust::FilterType::Undefined) { Ok(_) => match magick_image.crop_image(size, size, ops.x_offset, ops.y_offset) { Ok(_) => { let mut dir = fs::DirBuilder::new(); let _ = dir.recursive(true).create(cached_image_path.parent().unwrap()); match cached_image_path.to_str() { Some(cache_str) => match magick_image.write_image(cache_str) { Ok(_) => Ok(cached_image_path), Err(e) => Err(e.to_string()), }, None => Err("Undefined filepath error".to_string()), } }, Err(_) => todo!(), } Err(e) => Err(e.to_string()), } }, Err(e) => Err(e.to_string()), } }, None => Err("Undefined filepath error".to_string()), } } fn main() { let cli = Cli::parse(); match get_random_image(&cli.directory, cli.recursive) { Ok(image_path) => { match output_path(image_path.clone()) { Ok(cached_image_path) => match cached_image_path.exists() { true => println!("{}",cached_image_path.to_str().unwrap_or_default()), false => match scale_image(image_path, cached_image_path, cli.size) { Ok(final_path) => println!("{}",final_path.to_str().unwrap_or_default()), Err(e) => eprintln!("{}",e), }, }, Err(e) => eprintln!("{}",e), } }, Err(e) => eprintln!("{}",e), } }