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
61impl Display for ExitCode {
62 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
63 write!(f, "{:?}", self)
64 }
65}
66
67impl ExitCode {
68 pub fn as_integer_error_code(self) -> i32 {
71 self as i32
72 }
73}
74
75#[derive(Debug, Clone)]
77struct PreflateErrorInternal {
78 exit_code: ExitCode,
79 message: String,
80}
81
82#[derive(Clone)]
84pub struct PreflateError {
85 i: Box<PreflateErrorInternal>,
86}
87
88impl std::fmt::Debug for PreflateError {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 self.i.fmt(f)
92 }
93}
94
95pub type Result<T> = std::result::Result<T, PreflateError>;
96
97impl Display for PreflateError {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 write!(f, "{0}: {1}", self.i.exit_code, self.i.message)
100 }
101}
102
103impl PreflateError {
104 pub fn new(exit_code: ExitCode, message: impl AsRef<str>) -> PreflateError {
105 PreflateError {
106 i: Box::new(PreflateErrorInternal {
107 exit_code,
108 message: message.as_ref().to_owned(),
109 }),
110 }
111 }
112
113 pub fn exit_code(&self) -> ExitCode {
114 self.i.exit_code
115 }
116
117 pub fn message(&self) -> &str {
118 &self.i.message
119 }
120
121 #[cold]
122 #[inline(never)]
123 #[track_caller]
124 pub fn add_context(&mut self) {
125 self.i
126 .message
127 .push_str(&format!("\n at {}", std::panic::Location::caller()));
128 }
129}
130
131#[cold]
132#[track_caller]
133pub fn err_exit_code<T>(error_code: ExitCode, message: impl AsRef<str>) -> Result<T> {
134 let mut e = PreflateError::new(error_code, message.as_ref());
135 e.add_context();
136 return Err(e);
137}
138
139pub trait AddContext<T> {
140 #[track_caller]
141 fn context(self) -> Result<T>;
142 fn with_context<FN: Fn() -> String>(self, f: FN) -> Result<T>;
143}
144
145impl<T, E: Into<PreflateError>> AddContext<T> for core::result::Result<T, E> {
146 #[track_caller]
147 fn context(self) -> Result<T> {
148 match self {
149 Ok(x) => Ok(x),
150 Err(e) => {
151 let mut e = e.into();
152 e.add_context();
153 Err(e)
154 }
155 }
156 }
157
158 #[track_caller]
159 fn with_context<FN: Fn() -> String>(self, f: FN) -> Result<T> {
160 match self {
161 Ok(x) => Ok(x),
162 Err(e) => {
163 let mut e = e.into();
164 e.i.message.push_str(&f());
165 e.add_context();
166 Err(e)
167 }
168 }
169 }
170}
171
172impl std::error::Error for PreflateError {}
173
174fn get_io_error_exit_code(e: &std::io::Error) -> ExitCode {
175 if e.kind() == ErrorKind::UnexpectedEof {
176 ExitCode::ShortRead
177 } else {
178 ExitCode::OsError
179 }
180}
181
182impl From<TryFromIntError> for PreflateError {
183 #[track_caller]
184 fn from(e: TryFromIntError) -> Self {
185 let mut e = PreflateError::new(ExitCode::GeneralFailure, e.to_string().as_str());
186 e.add_context();
187 e
188 }
189}
190
191impl From<std::io::Error> for PreflateError {
193 #[track_caller]
194 fn from(e: std::io::Error) -> Self {
195 match e.downcast::<PreflateError>() {
196 Ok(le) => {
197 return le;
198 }
199 Err(e) => {
200 let mut e = PreflateError::new(get_io_error_exit_code(&e), e.to_string().as_str());
201 e.add_context();
202 e
203 }
204 }
205 }
206}
207
208impl From<PreflateError> for std::io::Error {
210 fn from(e: PreflateError) -> Self {
211 return std::io::Error::new(std::io::ErrorKind::Other, e);
212 }
213}
214
215#[test]
216fn test_error_translation() {
217 fn my_std_error() -> core::result::Result<(), std::io::Error> {
219 Err(PreflateError::new(ExitCode::AnalyzeFailed, "test error").into())
220 }
221
222 let e: PreflateError = my_std_error().unwrap_err().into();
223 assert_eq!(e.exit_code(), ExitCode::AnalyzeFailed);
224 assert_eq!(e.message(), "test error");
225
226 let e: PreflateError =
228 std::io::Error::new(std::io::ErrorKind::NotFound, "file not found").into();
229 assert_eq!(e.exit_code(), ExitCode::OsError);
230}