random-image-rs/src/main.rs
2025-09-22 19:13:26 -07:00

150 lines
5.3 KiB
Rust

#![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<bool>,
}
pub struct ImgOps {
width: usize,
height: usize,
x_offset: isize,
y_offset: isize
}
pub fn get_random_image(directory: &Path,recursive: Option<bool>) -> Result<PathBuf,String> {
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<PathBuf,String> {
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<PathBuf,String> {
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),
}
}