Add proper error handling and restructure code

Error handling using a custom error class was added. While adding error
handling, the majority of the code was restructured.
This commit is contained in:
Jean-Claude 2024-01-28 23:41:17 +01:00
parent ccd3d2c339
commit 84d6314e45
Signed by: jeanclaude
GPG Key ID: 8A300F57CBB9F63E
4 changed files with 107 additions and 53 deletions

33
Cargo.lock generated
View File

@ -168,18 +168,18 @@ checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
[[package]]
name = "proc-macro2"
version = "1.0.71"
version = "1.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@ -191,6 +191,7 @@ dependencies = [
"anyhow",
"clap",
"rexiv2",
"thiserror",
]
[[package]]
@ -212,15 +213,35 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "2.0.43"
version = "2.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53"
checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"

View File

@ -11,6 +11,7 @@ description = "Extract embedded thumbnail from raw image file"
anyhow = "1.0.77"
clap = { version = "4.4.12", features = ["derive"] }
rexiv2 = "0.10.0"
thiserror = "1.0.56"
[[bin]]
name = "rex"

15
src/error.rs Normal file
View File

@ -0,0 +1,15 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum General {
#[error("could not load metadata: {0}")]
LoadMetadata(rexiv2::Rexiv2Error),
#[error("not embedded thumbnail found")]
NoThumbnail,
#[error("could not store thumbnail: {0}")]
StoreThumbnail(rexiv2::Rexiv2Error),
#[error("could not get thumbnail extension: {0}")]
GetExtension(rexiv2::Rexiv2Error),
#[error("could not store metadata to thumbnail: {0}")]
StoreMetadata(rexiv2::Rexiv2Error),
}

View File

@ -1,62 +1,79 @@
#![feature(absolute_path)]
pub mod commands;
pub mod error;
use std::path::PathBuf;
use anyhow::Result;
use clap::Parser;
use rexiv2::Metadata;
use rexiv2::{Metadata, PreviewImage};
use commands::Cli;
use error::General;
/// Get preview with largest resolution.
fn get_largest_preview(metadata: &Metadata) -> Result<PreviewImage, General> {
let previews: Vec<PreviewImage> = metadata.get_preview_images().ok_or(General::NoThumbnail)?;
let largest_preview: PreviewImage = previews
.into_iter()
.max_by_key(|e| e.get_size())
.expect("previews is not supposed to be empty");
Ok(largest_preview)
}
/// Extract thumbnail of `src` and store to `output_dir`.
fn process_image(src: &PathBuf, output_dir: &Option<PathBuf>) -> Result<PathBuf, General> {
assert!(src.is_absolute());
let metadata = Metadata::new_from_path(src).map_err(|e| General::LoadMetadata(e))?;
let preview = get_largest_preview(&metadata)?;
let mut dst = match output_dir {
Some(e) => e.to_owned(),
None => src
.parent()
.expect("image is supposed to have a parent")
.to_owned(),
};
assert!(dst.is_dir());
// Set filename to name of source, without extension as `save_to_file` will add the appropriate extension
dst.push(
src.file_stem()
.expect("image is supposed to have a filename"),
);
preview
.save_to_file(&dst)
.map_err(|e| General::StoreThumbnail(e))?;
// Add correct extension
dst.set_extension(
preview
.get_extension()
.map_err(|e| General::GetExtension(e))?
.strip_prefix(".")
.expect("extension is supposed to start with ."),
);
assert!(dst.is_file());
metadata
.save_to_file(&dst)
.map_err(|e| General::StoreMetadata(e))?;
Ok(dst)
}
fn main() -> Result<()> {
let cli = Cli::parse();
for src in cli.input_images.iter() {
assert!(src.is_absolute());
println!("Processing image {:?}", src);
let metadata = Metadata::new_from_path(&src)?;
if let Some(previews) = metadata.get_preview_images() {
// Get preview with highest resolution
let preview = previews
.iter()
.max_by_key(|e| e.get_size())
.expect("previews is not supposed to be empty");
let mut dst = match cli.output_dir {
Some(ref e) => e.to_owned(),
None => src
.parent()
.expect("image is supposed to have a parent")
.to_owned(),
};
assert!(dst.is_dir());
// Set filename to name of source, without extension as `save_to_file` will add the appropriate extension
dst.push(
src.file_stem()
.expect("image is supposed to have a filename"),
);
preview
.save_to_file(&dst)
.expect("failed to save preview to file");
dst.set_extension(
preview
.get_extension()
.expect("failed to get preview extension")
.strip_prefix(".")
.expect("extension is supposed to start with ."),
);
assert!(dst.is_file());
metadata
.save_to_file(&dst)
.expect("failed to save metadata to file");
println!("Stored preview to {:?}", dst);
} else {
println!("Image {:?} contains no embedded JPG", src);
match process_image(src, &cli.output_dir) {
Ok(dst) => println!("Stored thumbnail to {:?}", dst),
Err(e) => {
println!("Error while processing image {:?}: {}", src, e);
}
}
}