preflate_rs/
preflate_error.rs1use std::fmt::Display;
8use std::io::ErrorKind;
9use std::num::TryFromIntError;
10
11#[derive(Debug, Clone, Copy, PartialEq)]
12#[non_exhaustive]
13pub enum ExitCode {
14 ReadDeflate = 1,
15 InvalidPredictionData = 2,
16 AnalyzeFailed = 3,
17 RecompressFailed = 4,
18 RoundtripMismatch = 5,
19 ReadBlock = 6,
20 PredictBlock = 7,
21 PredictTree = 8,
22 RecreateBlock = 9,
23 RecreateTree = 10,
24 EncodeBlock = 11,
25 InvalidCompressedWrapper = 12,
26 ZstdError = 14,
27 InvalidParameterHeader = 15,
28 ShortRead = 16,
29 OsError = 17,
30 GeneralFailure = 18,
31 InvalidIDat = 19,
32 MatchNotFound = 20,
33
34 InvalidDeflate = 21,
37
38 NoCompressionCandidates = 22,
43
44 InvalidParameter = 23,
45
46 AssertionFailure = 24,
48
49 NonZeroPadding = 25,
51
52 PredictionFailure = 26,
56
57 PlainTextLimit = 27,
59
60 WebPDecodeError = 28,
62
63 OutOfMemory = 29,
65}
66
67impl Display for ExitCode {
68 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
69 write!(f, "{:?}", self)
70 }
71}
72
73impl ExitCode {
74 pub fn as_integer_error_code(self) -> i32 {
77 self as i32
78 }
79}
80
81#[derive(Debug, Clone)]
83struct PreflateErrorInternal {
84 exit_code: ExitCode,
85 message: String,
86}
87
88#[derive(Clone)]
90pub struct PreflateError {
91 i: Box<PreflateErrorInternal>,
92}
93
94impl std::fmt::Debug for PreflateError {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 self.i.fmt(f)
98 }
99}
100
101pub type Result<T> = std::result::Result<T, PreflateError>;
102
103impl Display for PreflateError {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 write!(f, "{0}: {1}", self.i.exit_code, self.i.message)
106 }
107}
108
109impl PreflateError {
110 pub fn new(exit_code: ExitCode, message: impl AsRef<str>) -> PreflateError {
111 PreflateError {
112 i: Box::new(PreflateErrorInternal {
113 exit_code,
114 message: message.as_ref().to_owned(),
115 }),
116 }
117 }
118
119 pub fn exit_code(&self) -> ExitCode {
120 self.i.exit_code
121 }
122
123 pub fn message(&self) -> &str {
124 &self.i.message
125 }
126
127 #[cold]
128 #[inline(never)]
129 #[track_caller]
130 pub fn add_context(&mut self) {
131 self.i
132 .message
133 .push_str(&format!("\n at {}", std::panic::Location::caller()));
134 }
135}
136
137#[cold]
138#[track_caller]
139pub fn err_exit_code<T>(error_code: ExitCode, message: impl AsRef<str>) -> Result<T> {
140 let mut e = PreflateError::new(error_code, message.as_ref());
141 e.add_context();
142 return Err(e);
143}
144
145pub trait AddContext<T> {
146 #[track_caller]
147 fn context(self) -> Result<T>;
148 fn with_context<FN: Fn() -> String>(self, f: FN) -> Result<T>;
149}
150
151impl<T, E: Into<PreflateError>> AddContext<T> for core::result::Result<T, E> {
152 #[track_caller]
153 fn context(self) -> Result<T> {
154 match self {
155 Ok(x) => Ok(x),
156 Err(e) => {
157 let mut e = e.into();
158 e.add_context();
159 Err(e)
160 }
161 }
162 }
163
164 #[track_caller]
165 fn with_context<FN: Fn() -> String>(self, f: FN) -> Result<T> {
166 match self {
167 Ok(x) => Ok(x),
168 Err(e) => {
169 let mut e = e.into();
170 e.i.message.push_str(&f());
171 e.add_context();
172 Err(e)
173 }
174 }
175 }
176}
177
178impl std::error::Error for PreflateError {}
179
180fn get_io_error_exit_code(e: &std::io::Error) -> ExitCode {
181 if e.kind() == ErrorKind::UnexpectedEof {
182 ExitCode::ShortRead
183 } else {
184 ExitCode::OsError
185 }
186}
187
188impl From<TryFromIntError> for PreflateError {
189 #[track_caller]
190 fn from(e: TryFromIntError) -> Self {
191 let mut e = PreflateError::new(ExitCode::GeneralFailure, e.to_string().as_str());
192 e.add_context();
193 e
194 }
195}
196
197impl From<std::io::Error> for PreflateError {
199 #[track_caller]
200 fn from(e: std::io::Error) -> Self {
201 match e.downcast::<PreflateError>() {
202 Ok(le) => {
203 return le;
204 }
205 Err(e) => {
206 let mut e = PreflateError::new(get_io_error_exit_code(&e), e.to_string().as_str());
207 e.add_context();
208 e
209 }
210 }
211 }
212}
213
214impl From<PreflateError> for std::io::Error {
216 fn from(e: PreflateError) -> Self {
217 return std::io::Error::new(std::io::ErrorKind::Other, e);
218 }
219}
220
221#[test]
222fn test_error_translation() {
223 fn my_std_error() -> core::result::Result<(), std::io::Error> {
225 Err(PreflateError::new(ExitCode::AnalyzeFailed, "test error").into())
226 }
227
228 let e: PreflateError = my_std_error().unwrap_err().into();
229 assert_eq!(e.exit_code(), ExitCode::AnalyzeFailed);
230 assert_eq!(e.message(), "test error");
231
232 let e: PreflateError =
234 std::io::Error::new(std::io::ErrorKind::NotFound, "file not found").into();
235 assert_eq!(e.exit_code(), ExitCode::OsError);
236}