Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved error handling #4

Merged
merged 13 commits into from
Dec 13, 2024
27 changes: 24 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ console = "0.15.8"
dialoguer = "0.11.0"
image = "0.25.5"
indicatif = "0.17.9"
thiserror = "2.0.6"

# The profile that 'cargo dist' will build with
[profile.dist]
Expand Down
9 changes: 5 additions & 4 deletions src/app/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ use resize::ResizeArgs;
use transparent::TransparentArgs;

use clap::Parser;
use std::error::Error;
use std::path::PathBuf;

use anyhow::Result;

use crate::backend::error::AppError;

use super::run::{RunBatch, RunSingle};

#[derive(Parser)]
Expand Down Expand Up @@ -44,7 +45,7 @@ pub struct CommandArgs {
}

#[derive(Parser, Debug)]
struct ExtraArgs {
pub struct ExtraArgs {
/// Overwrite existing images
#[clap(short = 'x', long, global = true)]
pub overwrite: bool,
Expand Down Expand Up @@ -83,12 +84,12 @@ pub enum Command {
}

impl CommandArgs {
pub fn run(&self) -> Result<(), Box<dyn Error>> {
pub fn run(&self) -> Result<()> {
match &self.command {
Command::Completions(args) => args.run(),
Command::Info(args) => args.run(),
_ => match self.images.len() {
0 => Err("Please add images".into()),
0 => Err(AppError::NoImages.into()),
1 => Ok(self.run_single()?),
_ => Ok(self.run_batch()?),
},
Expand Down
11 changes: 6 additions & 5 deletions src/app/command/completions.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::app;
use crate::backend::error::AppError;
use anyhow::Result;
use clap::{CommandFactory, Parser};
use clap_complete::{generate, Shell};
use clap_complete_nushell::Nushell;
use std::error::Error;
use std::io::stdout;
use std::str::FromStr;

Expand All @@ -14,13 +15,13 @@ pub struct CompletionArgs {
}

impl CompletionArgs {
pub fn run(&self) -> Result<(), Box<dyn Error>> {
pub fn run(&self) -> Result<()> {
match self.shell.return_shell() {
Ok((None, Some(shell))) => {
generate(shell, &mut app::Args::command(), "rimi", &mut stdout())
}
Ok((_, None)) => generate(Nushell, &mut app::Args::command(), "rimi", &mut stdout()),
_ => return Err("Unkown shell".into()),
_ => return Err(AppError::UnknownShell.into()),
}
Ok(())
}
Expand Down Expand Up @@ -62,7 +63,7 @@ impl FromStr for ShellExt {
}

impl ShellExt {
fn return_shell(&self) -> Result<(Option<Nushell>, Option<Shell>), Box<dyn Error>> {
fn return_shell(&self) -> Result<(Option<Nushell>, Option<Shell>)> {
match self.name.to_uppercase().as_str() {
"BASH" => Ok((None, Some(Shell::Bash))),
"ZSH" => Ok((None, Some(Shell::Zsh))),
Expand All @@ -71,7 +72,7 @@ impl ShellExt {
"FISH" => Ok((None, Some(Shell::Fish))),
"NUSHELL" => Ok((Some(Nushell), None)),
"ELVISH" => Ok((None, Some(Shell::Elvish))),
_ => Err("Unknown shell".into()),
_ => Err(AppError::UnknownShell.into()),
}
}
}
10 changes: 7 additions & 3 deletions src/app/command/info.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::backend::error::TaskError;
use crate::{utils::image::open_image, utils::info::print_info};
use anyhow::Result;
use clap::Parser;
use std::error::Error;
use std::path::PathBuf;

#[derive(Parser, Debug)]
Expand All @@ -14,8 +15,11 @@ pub struct InfoArgs {
}

impl InfoArgs {
pub fn run(&self) -> Result<(), Box<dyn Error>> {
let image = open_image(self.image_file.clone())?;
pub fn run(&self) -> Result<()> {
let image = match open_image(self.image_file.clone()) {
Ok(image) => image,
Err(e) => return Err(TaskError::SingleError(e).into()),
};
print_info(&image, self.image_file.to_path_buf(), self.short);
Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions src/app/command/recolor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::utils::color::ColorInfo;
use anyhow::Result;
use clap::Parser;
use image::DynamicImage;
use std::error::Error;

#[derive(Parser, Debug)]
pub struct RecolorArgs {
Expand All @@ -11,7 +11,7 @@ pub struct RecolorArgs {
}

impl RecolorArgs {
pub fn run(&self, image: &mut DynamicImage) -> Result<(), Box<dyn Error>> {
pub fn run(&self, image: &mut DynamicImage) -> Result<()> {
self.color_type.convert_image(image);
Ok(())
}
Expand Down
13 changes: 8 additions & 5 deletions src/app/command/resize.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::backend::error::TaskError;
use crate::utils::image::{resize_image, Dimensions};
use anyhow::Result;
use clap::Parser;
use image::DynamicImage;
use std::error::Error;

#[derive(Parser, Debug)]
pub struct ResizeArgs {
Expand All @@ -21,16 +22,18 @@ pub struct ResizeArgs {
}

impl ResizeArgs {
pub fn run(&self, image: &mut DynamicImage) -> Result<(), Box<dyn Error>> {
resize_image(
pub fn run(&self, image: &mut DynamicImage) -> Result<()> {
match resize_image(
image,
Dimensions {
x: self.width,
y: self.height,
},
self.filter.to_string(),
self.preserve_aspect,
)?;
Ok(())
) {
Ok(()) => Ok(()),
Err(e) => Err(TaskError::SingleError(e).into()),
}
}
}
4 changes: 2 additions & 2 deletions src/app/command/transparent.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use crate::utils::image::remove_background;
use anyhow::Result;
use clap::Parser;
use image::DynamicImage;
use std::error::Error;

#[derive(Parser, Debug)]
pub struct TransparentArgs {}

impl TransparentArgs {
pub fn run(&self, image: &mut DynamicImage) -> Result<(), Box<dyn Error>> {
pub fn run(&self, image: &mut DynamicImage) -> Result<()> {
remove_background(image);
Ok(())
}
Expand Down
35 changes: 16 additions & 19 deletions src/app/run/batch.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::path::PathBuf;

use crate::app::command::{Command, CommandArgs};
use crate::backend::error::TaskError;
use crate::backend::progress::{AppProgress, BatchProgress};
use crate::backend::queue::{TaskQueue, TaskState};

Expand Down Expand Up @@ -39,15 +40,7 @@ impl RunBatch for CommandArgs {
tasks_queue.set_decoded(&good_image, task_id);
}
Err(error) => {
batch_progress.error_sub_operation(
format!(
"[{}/{}] Failed to decode image: {:?}",
index,
num_images,
image.file_name().as_slice()
)
.as_str(),
);
batch_progress.error_sub_operation(error.as_str());
tasks_queue.set_failed(task_id, error);
}
}
Expand All @@ -67,11 +60,11 @@ impl RunBatch for CommandArgs {
let mut total_errors = Vec::new();
for task in tasks_queue.failed_tasks().iter() {
if let TaskState::Failed(error) = &task.state {
total_errors.push(TaskError(error.0.clone()));
total_errors.push(error.to_string());
}
}

return Err(Box::new(BatchErrors(total_errors)));
return Err(TaskError::BatchError(total_errors).into());
}
} else {
batch_progress.complete_operation_with_message(
Expand All @@ -95,11 +88,14 @@ impl RunBatch for CommandArgs {
decoded_paths.push(task.image_path.to_path_buf());
}

let out_paths = create_paths(
let out_paths = match create_paths(
decoded_paths,
output_path,
self.extra_args.name_expr.as_deref(),
)?;
) {
Ok(out_paths) => out_paths,
Err(e) => return Err(TaskError::SingleError(e).into()),
};

for (index, path) in out_paths.iter().enumerate() {
tasks_queue.set_out_path(tasks_queue.decoded_ids()[index], path);
Expand All @@ -111,15 +107,13 @@ impl RunBatch for CommandArgs {

batch_progress.start_operation(format!("Processing {} images", count).as_str());

for index in 0..count {
for _ in 0..count {
let task_id = tasks_queue.decoded_tasks()[0].id;

let count = count - 1;

let mut current_task = {
match tasks_queue.task_by_id_mut(task_id) {
Some(task) => task.clone(),
_ => return Err("No such task".into()),
_ => return Err(TaskError::NoSuchTask.into()),
}
};

Expand Down Expand Up @@ -201,7 +195,11 @@ impl RunBatch for CommandArgs {
}
}
command => {
return Err(format!("{:?} cannot be run right now", command).into());
return Err(TaskError::SingleError(format!(
"{:?} command cannot be run here.",
command
))
.into());
}
};

Expand Down Expand Up @@ -251,7 +249,6 @@ impl RunBatch for CommandArgs {
}
if !tasks_queue.completed_tasks().is_empty() {
batch_progress.complete();
t
}
Ok(())
}
Expand Down
5 changes: 3 additions & 2 deletions src/app/run/single.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::RunSingle;
use crate::app::command::{Command, CommandArgs};
use crate::backend::error::TaskError;
use crate::backend::progress::AppProgress;
use crate::backend::progress::SingleProgress;
use console::Style;
Expand Down Expand Up @@ -33,7 +34,7 @@ impl RunSingle for CommandArgs {
}
Err(e) => {
single_progress.abort_message("Image decode failed");
return Err(e.into());
return Err(TaskError::SingleError(e).into());
}
};

Expand Down Expand Up @@ -126,7 +127,7 @@ impl RunSingle for CommandArgs {
Ok(()) => single_progress.complete_operation_with_message("Image saved successfully"),
Err(e) => {
single_progress.abort_message("Image failed to save");
return Err(e.into());
return Err(TaskError::SingleError(e).into());
}
}
single_progress.complete();
Expand Down
2 changes: 1 addition & 1 deletion src/backend.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
mod error;
pub mod error;
pub mod progress;
pub mod queue;
22 changes: 22 additions & 0 deletions src/backend/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use thiserror::Error;

#[derive(Error, Debug, Clone, PartialEq)]
pub enum TaskError {
#[error("Image task failed: {0}")]
SingleError(String),

#[error("Multiple operations failed: {0:?}")]
BatchError(Vec<String>),

#[error("No such task")]
NoSuchTask,
}

#[derive(Error, Debug)]
pub enum AppError {
#[error("Unknown shell")]
UnknownShell,

#[error("No images selected")]
NoImages,
}
Loading