use thiserror::Error;
#[derive(Error, Debug)]
pub enum PinnerError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("API error: {0}")]
Api(String),
#[error("Parse error: {0}")]
Parse(String),
#[error("Path not found: {0}")]
PathNotFound(String),
#[error("Verification failed: {0}")]
VerificationFailed(String),
#[error("Ignore error: {0}")]
Ignore(#[from] ignore::Error),
#[error("Config error: {0}")]
Config(String),
#[error("Rate limit error: {0}")]
RateLimit(String),
#[error("Authentication error: {0}")]
Authentication(String),
#[error("Unsupported operation: {0}")]
Unsupported(String),
#[error("Registry error: {0}")]
Registry(String),
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("Middleware error: {0}")]
Middleware(#[from] reqwest_middleware::Error),
#[error("Offline error: {0}")]
Offline(String),
}
impl PinnerError {
pub fn is_path_not_found(&self) -> bool {
matches!(self, PinnerError::PathNotFound(_))
}
pub fn is_fatal(&self) -> bool {
match self {
PinnerError::RateLimit(_) => true,
PinnerError::Authentication(_) => true,
PinnerError::Config(_) => true,
PinnerError::Io(_) => true,
PinnerError::Parse(_) => true,
PinnerError::PathNotFound(_) => true,
PinnerError::VerificationFailed(_) => true,
PinnerError::Ignore(_) => true,
PinnerError::Unsupported(_) => true,
PinnerError::Offline(_) => true,
PinnerError::Api(_) => false,
PinnerError::Registry(_) => false,
PinnerError::Http(_) => false,
PinnerError::Middleware(_) => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io;
#[test]
fn test_error_display() {
let err = PinnerError::Api("failed".to_string());
assert_eq!(format!("{}", err), "API error: failed");
assert!(!err.is_path_not_found());
let err = PinnerError::Parse("yaml".to_string());
assert_eq!(format!("{}", err), "Parse error: yaml");
let err = PinnerError::PathNotFound("path/to/wf".to_string());
assert_eq!(format!("{}", err), "Path not found: path/to/wf");
assert!(err.is_path_not_found());
let err = PinnerError::Config("invalid".to_string());
assert_eq!(format!("{}", err), "Config error: invalid");
let err = PinnerError::VerificationFailed("unpinned".to_string());
assert_eq!(format!("{}", err), "Verification failed: unpinned");
let err = PinnerError::RateLimit("exceeded".to_string());
assert_eq!(format!("{}", err), "Rate limit error: exceeded");
}
#[test]
fn test_error_is_fatal() {
assert!(PinnerError::RateLimit("".into()).is_fatal());
assert!(PinnerError::Config("".into()).is_fatal());
assert!(PinnerError::Io(io::Error::other("")).is_fatal());
assert!(PinnerError::Parse("".into()).is_fatal());
assert!(PinnerError::PathNotFound("".into()).is_fatal());
assert!(PinnerError::VerificationFailed("".into()).is_fatal());
assert!(PinnerError::Ignore(ignore::Error::Io(io::Error::other(""))).is_fatal());
assert!(!PinnerError::Api("".into()).is_fatal());
}
#[test]
fn test_error_from_io() {
let io_err = io::Error::new(io::ErrorKind::NotFound, "not found");
let err = PinnerError::from(io_err);
assert!(matches!(err, PinnerError::Io(_)));
assert!(format!("{}", err).contains("IO error: not found"));
}
#[test]
fn test_error_from_ignore() {
let io_err = io::Error::other("ignore err");
let err = PinnerError::Ignore(ignore::Error::Io(io_err));
assert!(format!("{}", err).contains("Ignore error"));
}
}