use crate::{Colors, Dimension, Orientation};
use std::{
io,
num::{ParseIntError, TryFromIntError},
path::PathBuf,
string::FromUtf8Error,
};
use thiserror::Error;
pub type WallSwitchResult<T> = Result<T, WallSwitchError>;
#[derive(Error, Debug)]
pub enum WallSwitchError {
#[error(
"{e}: '{a}' value '{v}' must be at least '{n}'. \
The condition ({v} >= {n}) is false.\n\n\
For more information, try '{h}'.",
e = "Error".red().bold(),
v = value.yellow(),
a = arg.yellow(),
n = num.green(),
h = "--help".green(),
)]
AtLeastValue {
arg: String,
value: String,
num: u64,
},
#[error(
"{e}: '{a}' value '{v}' must be at most '{n}'. \
The condition ({v} <= {n}) is false.\n\n\
For more information, try '{h}'.",
e = "Error".red().bold(),
v = value.yellow(),
a = arg.yellow(),
n = num.green(),
h = "--help".green(),
)]
AtMostValue {
arg: String,
value: String,
num: u64,
},
#[error(
"{e}: Command '{program}' failed!\n\
Status: {status}\n\
Error details: {stderr}",
e = "Error".red().bold(),
)]
CommandFailed {
program: String,
status: String,
stderr: String,
},
#[error("{e}: Could not detect desktop environment. Please set DESKTOP_SESSION manually.", e = "Error".red().bold())]
DesktopDetectionFailed,
#[error("Disregard the path: '{p}'.", p = .0.display().yellow(),)]
DisregardPath(PathBuf),
#[error("{e}: Environment variable '{0}' not found.", e = "Error".red().bold())]
EnvVarMissing(String),
#[error("Failed to convert command output to UTF-8: {0}")]
FromUtf8(#[from] FromUtf8Error),
#[error("IO error: {0}")]
Io(#[from] io::Error),
#[error("{0}")]
InvalidDimension(#[from] DimensionError),
#[error(
"{e}: Invalid file name --> Disregard the path: '{p}'.",
e = "Error".red().bold(),
p = .0.display().yellow(),
)]
InvalidFilename(PathBuf),
#[error(
"{e}: invalid file size '{s}' bytes. \
The condition ({min} <= {s} <= {max}) is false.",
e = "Error".red().bold(),
min = min_size.green(),
max = max_size.green(),
s = size.yellow(),
)]
InvalidSize {
min_size: u64,
size: u64,
max_size: u64,
},
#[error(
"{e}: invalid value '{v}' for '{a}'.\n\n\
For more information, try '{h}'.",
e = "Error".red().bold(),
v = value.yellow(),
a = arg.green(),
h = "--help".green(),
)]
InvalidValue { arg: String, value: String },
#[error("{e}: Failed to create file {path:?}\n{io_error}", e = "Error".red().bold())]
IOError {
path: PathBuf,
#[source]
io_error: io::Error,
},
#[error("JSON serialization/deserialization error: {0}")]
Json(#[from] serde_json::Error),
#[error("Unable to obtain maximum value!")]
MaxValue,
#[error("Unable to obtain minimum value!")]
MinValue,
#[error(
"{e}: missing value for '{a}'.\n\n\
For more information, try '{h}'.",
e = "Error".red().bold(),
a = arg.yellow(),
h = "--help".green(),
)]
MissingValue { arg: String },
#[error(
"{e}: min ({min}) must be less than or equal to max ({max})\n\
The condition ({min} <= {max}) is false.",
e = "Error".red().bold()
)]
MinMax { min: u64, max: u64 },
#[error("Awww daemon failed to start or is unresponsive: {0}")]
AwwwDaemonError(String),
#[error(
"{e}: No Wayland wallpaper utility was found on your system.\n\n\
To fix this, please install at least one of them:\n\
- Manjaro/Arch: {pacman}\n\
- Fedora: {dnf}\n\
- Debian/Ubuntu: {apt}",
e = "Error".red().bold(),
pacman = "sudo pacman -S awww swaybg hyprpaper".green(),
dnf = "sudo dnf install awww swaybg hyprpaper".green(),
apt = "sudo apt install awww swaybg hyprpaper".green(),
)]
MissingWaylandTools,
#[error(
"{e}: no images found in image directories!\n\
directories: {paths:#?}",
e = "Error".red().bold(),
)]
NoImages { paths: Vec<PathBuf> },
#[error(
"{e}: no active monitors detected via '{tool}'!",
e = "Error".red().bold(),
tool = .0.yellow(),
)]
NoMonitors(String),
#[error(
"invalid orientation.\n\n\
Valid options: [{h}, {v}]",
h = Orientation::Horizontal.green(),
v = Orientation::Vertical.green(),
)]
InvalidOrientation,
#[error(
"{e}: insufficient number of image files!\n\
Found only {n} image file(s):\n\
{paths:#?}",
n = nfiles.yellow(),
e = "Error".red().bold(),
)]
InsufficientImages { paths: Vec<PathBuf>, nfiles: usize },
#[error("Insufficient number of valid images!")]
InsufficientNumber,
#[error("Wallpaper dir {0:?} does not exist.")]
Parent(PathBuf),
#[error("{0}")]
TryInto(String),
#[error("Unable to find '{0}'!")]
UnableToFind(String),
#[error(
"{e}: unexpected argument '{a}' found.\n\n\
For more information, try '{h}'.",
e = "Error".red().bold(),
a = arg.yellow(),
h = "--help".green(),
)]
UnexpectedArg { arg: String },
}
impl From<TryFromIntError> for WallSwitchError {
fn from(err: TryFromIntError) -> Self {
Self::TryInto(err.to_string())
}
}
#[derive(Error, Debug)]
pub enum DimensionError {
#[error(
"{error}: invalid dimension '{dimension}'.\n\
{log_min}{log_max}\
Disregard the path: '{path}'\n",
error = "Error".red().bold(),
dimension = .dimension.yellow(),
log_min = .log_min,
log_max = .log_max,
path = .path.display().yellow(),
)]
DimensionFormatError {
dimension: Dimension,
log_min: String,
log_max: String,
path: PathBuf,
},
#[error("Invalid dimension format '{0}': failed to parse integer - {1}")]
InvalidParse(String, #[source] ParseIntError),
#[error("Invalid dimension format: expected two numbers (width x height)")]
InvalidFormat,
#[error("Zero is not a valid dimension component")]
ZeroDimension,
}
#[cfg(test)]
mod error_tests {
use crate::{Colors, WallSwitchError};
use std::path::PathBuf;
#[test]
fn test_error_display() {
let path = PathBuf::from("/tmp");
let text = format!("Disregard the path: '{}'.", path.display().yellow());
println!("text: {text}");
assert_eq!(
WallSwitchError::InsufficientNumber.to_string(),
"Insufficient number of valid images!"
);
assert_eq!(WallSwitchError::DisregardPath(path).to_string(), text);
}
}