Skip to main content

panimg_core/
error.rs

1use serde::Serialize;
2use std::path::PathBuf;
3
4/// Exit codes for structured error reporting.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
6#[serde(into = "u8")]
7pub enum ExitCode {
8    Success = 0,
9    General = 1,
10    InputFile = 2,
11    OutputIssue = 3,
12    Unsupported = 4,
13    BadArgs = 5,
14}
15
16impl From<ExitCode> for u8 {
17    fn from(code: ExitCode) -> u8 {
18        code as u8
19    }
20}
21
22/// Structured error type for panimg.
23#[derive(Debug, thiserror::Error, Serialize)]
24#[serde(tag = "error", rename_all = "snake_case")]
25pub enum PanimgError {
26    #[error("file not found: {path}")]
27    FileNotFound { path: PathBuf, suggestion: String },
28
29    #[error("permission denied: {path}")]
30    PermissionDenied { path: PathBuf, suggestion: String },
31
32    #[error("unsupported format: {format}")]
33    UnsupportedFormat { format: String, suggestion: String },
34
35    #[error("unknown format for: {path}")]
36    UnknownFormat { path: PathBuf, suggestion: String },
37
38    #[error("decode error: {message}")]
39    DecodeError {
40        message: String,
41        #[serde(skip_serializing_if = "Option::is_none")]
42        path: Option<PathBuf>,
43        suggestion: String,
44    },
45
46    #[error("encode error: {message}")]
47    EncodeError {
48        message: String,
49        #[serde(skip_serializing_if = "Option::is_none")]
50        path: Option<PathBuf>,
51        suggestion: String,
52    },
53
54    #[error("output already exists: {path}")]
55    OutputExists { path: PathBuf, suggestion: String },
56
57    #[error("invalid argument: {message}")]
58    InvalidArgument { message: String, suggestion: String },
59
60    #[error("resize error: {message}")]
61    ResizeError { message: String, suggestion: String },
62
63    #[error("io error: {message}")]
64    IoError {
65        message: String,
66        #[serde(skip_serializing_if = "Option::is_none")]
67        path: Option<PathBuf>,
68        suggestion: String,
69    },
70}
71
72impl PanimgError {
73    pub fn exit_code(&self) -> ExitCode {
74        match self {
75            Self::FileNotFound { .. } | Self::PermissionDenied { .. } => ExitCode::InputFile,
76            Self::OutputExists { .. } => ExitCode::OutputIssue,
77            Self::UnsupportedFormat { .. } | Self::UnknownFormat { .. } => ExitCode::Unsupported,
78            Self::InvalidArgument { .. } => ExitCode::BadArgs,
79            Self::DecodeError { .. } => ExitCode::InputFile,
80            Self::EncodeError { .. } | Self::IoError { .. } => ExitCode::OutputIssue,
81            Self::ResizeError { .. } => ExitCode::General,
82        }
83    }
84
85    pub fn suggestion(&self) -> &str {
86        match self {
87            Self::FileNotFound { suggestion, .. }
88            | Self::PermissionDenied { suggestion, .. }
89            | Self::UnsupportedFormat { suggestion, .. }
90            | Self::UnknownFormat { suggestion, .. }
91            | Self::DecodeError { suggestion, .. }
92            | Self::EncodeError { suggestion, .. }
93            | Self::OutputExists { suggestion, .. }
94            | Self::InvalidArgument { suggestion, .. }
95            | Self::ResizeError { suggestion, .. }
96            | Self::IoError { suggestion, .. } => suggestion,
97        }
98    }
99}
100
101pub type Result<T> = std::result::Result<T, PanimgError>;