1use core::fmt;
7
8pub type Result<T> = core::result::Result<T, Error>;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ErrorClass {
14 Io,
16 Usage,
18 InvalidContainer,
20 UnsupportedVersion,
22 UnsupportedFeature,
24 IntegrityMismatch,
26 ResourceLimit,
28 InvalidGraph,
30 InvalidModel,
32 EntropyDecode,
34 InvalidPdfStructure,
36 CodecReplay,
38 MissingExternalObject,
40 CoverageViolation,
42 ReconstructionMismatch,
44 Cancelled,
46 InvalidZipStructure,
50 InvalidXmlStructure,
53 InvalidPackageStructure,
57 InvalidJsonStructure,
61 InvalidYamlStructure,
65 InvalidCsvStructure,
69 InvalidMarkdownStructure,
73 InvalidHtmlStructure,
78 InvalidTomlStructure,
82 InvalidJsonlStructure,
86 InvalidEmlStructure,
90 InvalidParquetStructure,
95 InvalidArrowStructure,
99 InternalInvariant,
101}
102
103impl ErrorClass {
104 pub const fn exit_code(self) -> i32 {
106 match self {
107 ErrorClass::Io => 3,
108 ErrorClass::Usage => 2,
109 ErrorClass::InvalidContainer => 4,
110 ErrorClass::UnsupportedVersion => 5,
111 ErrorClass::UnsupportedFeature => 6,
112 ErrorClass::IntegrityMismatch => 7,
113 ErrorClass::ResourceLimit => 8,
114 ErrorClass::InvalidGraph => 9,
115 ErrorClass::InvalidModel => 10,
116 ErrorClass::EntropyDecode => 11,
117 ErrorClass::InvalidPdfStructure => 12,
118 ErrorClass::CodecReplay => 13,
119 ErrorClass::MissingExternalObject => 14,
120 ErrorClass::CoverageViolation => 15,
121 ErrorClass::ReconstructionMismatch => 16,
122 ErrorClass::Cancelled => 17,
123 ErrorClass::InvalidZipStructure => 18,
124 ErrorClass::InvalidXmlStructure => 19,
125 ErrorClass::InvalidPackageStructure => 20,
126 ErrorClass::InvalidJsonStructure => 21,
127 ErrorClass::InvalidYamlStructure => 22,
128 ErrorClass::InvalidCsvStructure => 23,
129 ErrorClass::InvalidMarkdownStructure => 24,
130 ErrorClass::InvalidHtmlStructure => 25,
131 ErrorClass::InvalidTomlStructure => 26,
132 ErrorClass::InvalidJsonlStructure => 27,
133 ErrorClass::InvalidEmlStructure => 28,
134 ErrorClass::InvalidParquetStructure => 29,
135 ErrorClass::InvalidArrowStructure => 30,
136 ErrorClass::InternalInvariant => 70,
137 }
138 }
139
140 pub const fn as_str(self) -> &'static str {
142 match self {
143 ErrorClass::Io => "Io",
144 ErrorClass::Usage => "Usage",
145 ErrorClass::InvalidContainer => "InvalidContainer",
146 ErrorClass::UnsupportedVersion => "UnsupportedVersion",
147 ErrorClass::UnsupportedFeature => "UnsupportedFeature",
148 ErrorClass::IntegrityMismatch => "IntegrityMismatch",
149 ErrorClass::ResourceLimit => "ResourceLimit",
150 ErrorClass::InvalidGraph => "InvalidGraph",
151 ErrorClass::InvalidModel => "InvalidModel",
152 ErrorClass::EntropyDecode => "EntropyDecode",
153 ErrorClass::InvalidPdfStructure => "InvalidPdfStructure",
154 ErrorClass::CodecReplay => "CodecReplay",
155 ErrorClass::MissingExternalObject => "MissingExternalObject",
156 ErrorClass::CoverageViolation => "CoverageViolation",
157 ErrorClass::ReconstructionMismatch => "ReconstructionMismatch",
158 ErrorClass::Cancelled => "Cancelled",
159 ErrorClass::InvalidZipStructure => "InvalidZipStructure",
160 ErrorClass::InvalidXmlStructure => "InvalidXmlStructure",
161 ErrorClass::InvalidPackageStructure => "InvalidPackageStructure",
162 ErrorClass::InvalidJsonStructure => "InvalidJsonStructure",
163 ErrorClass::InvalidYamlStructure => "InvalidYamlStructure",
164 ErrorClass::InvalidCsvStructure => "InvalidCsvStructure",
165 ErrorClass::InvalidMarkdownStructure => "InvalidMarkdownStructure",
166 ErrorClass::InvalidHtmlStructure => "InvalidHtmlStructure",
167 ErrorClass::InvalidTomlStructure => "InvalidTomlStructure",
168 ErrorClass::InvalidJsonlStructure => "InvalidJsonlStructure",
169 ErrorClass::InvalidEmlStructure => "InvalidEmlStructure",
170 ErrorClass::InvalidParquetStructure => "InvalidParquetStructure",
171 ErrorClass::InvalidArrowStructure => "InvalidArrowStructure",
172 ErrorClass::InternalInvariant => "InternalInvariant",
173 }
174 }
175}
176
177#[derive(Debug, Clone)]
179pub struct Error {
180 class: ErrorClass,
181 message: String,
182}
183
184impl Error {
185 pub fn new(class: ErrorClass, message: impl Into<String>) -> Self {
187 Error {
188 class,
189 message: message.into(),
190 }
191 }
192
193 pub fn class(&self) -> ErrorClass {
195 self.class
196 }
197
198 pub fn exit_code(&self) -> i32 {
200 self.class.exit_code()
201 }
202
203 pub fn message(&self) -> &str {
205 &self.message
206 }
207}
208
209impl fmt::Display for Error {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 write!(f, "{}: {}", self.class.as_str(), self.message)
212 }
213}
214
215impl std::error::Error for Error {}
216
217impl From<std::io::Error> for Error {
218 fn from(e: std::io::Error) -> Self {
219 Error::new(ErrorClass::Io, e.to_string())
220 }
221}
222
223macro_rules! ctor {
224 ($name:ident, $class:ident) => {
225 pub fn $name(message: impl Into<String>) -> Self {
227 Error::new(ErrorClass::$class, message)
228 }
229 };
230}
231
232impl Error {
233 ctor!(io, Io);
234 ctor!(usage, Usage);
235 ctor!(invalid_container, InvalidContainer);
236 ctor!(unsupported_version, UnsupportedVersion);
237 ctor!(unsupported_feature, UnsupportedFeature);
238 ctor!(integrity_mismatch, IntegrityMismatch);
239 ctor!(resource_limit, ResourceLimit);
240 ctor!(invalid_graph, InvalidGraph);
241 ctor!(invalid_model, InvalidModel);
242 ctor!(entropy_decode, EntropyDecode);
243 ctor!(invalid_pdf_structure, InvalidPdfStructure);
244 ctor!(codec_replay, CodecReplay);
245 ctor!(missing_external_object, MissingExternalObject);
246 ctor!(coverage_violation, CoverageViolation);
247 ctor!(reconstruction_mismatch, ReconstructionMismatch);
248 ctor!(cancelled, Cancelled);
249 ctor!(invalid_zip_structure, InvalidZipStructure);
250 ctor!(invalid_xml_structure, InvalidXmlStructure);
251 ctor!(invalid_package_structure, InvalidPackageStructure);
252 ctor!(invalid_json_structure, InvalidJsonStructure);
253 ctor!(invalid_yaml_structure, InvalidYamlStructure);
254 ctor!(invalid_csv_structure, InvalidCsvStructure);
255 ctor!(invalid_markdown_structure, InvalidMarkdownStructure);
256 ctor!(invalid_html_structure, InvalidHtmlStructure);
257 ctor!(invalid_toml_structure, InvalidTomlStructure);
258 ctor!(invalid_jsonl_structure, InvalidJsonlStructure);
259 ctor!(invalid_eml_structure, InvalidEmlStructure);
260 ctor!(invalid_parquet_structure, InvalidParquetStructure);
261 ctor!(invalid_arrow_structure, InvalidArrowStructure);
262 ctor!(internal_invariant, InternalInvariant);
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268
269 #[test]
270 fn exit_codes_are_distinct_and_stable() {
271 let classes = [
273 ErrorClass::Io,
274 ErrorClass::Usage,
275 ErrorClass::InvalidContainer,
276 ErrorClass::UnsupportedVersion,
277 ErrorClass::UnsupportedFeature,
278 ErrorClass::IntegrityMismatch,
279 ErrorClass::ResourceLimit,
280 ErrorClass::InvalidGraph,
281 ErrorClass::InvalidModel,
282 ErrorClass::EntropyDecode,
283 ErrorClass::InvalidPdfStructure,
284 ErrorClass::CodecReplay,
285 ErrorClass::MissingExternalObject,
286 ErrorClass::CoverageViolation,
287 ErrorClass::ReconstructionMismatch,
288 ErrorClass::Cancelled,
289 ErrorClass::InvalidZipStructure,
290 ErrorClass::InvalidXmlStructure,
291 ErrorClass::InvalidPackageStructure,
292 ErrorClass::InvalidJsonStructure,
293 ErrorClass::InvalidYamlStructure,
294 ErrorClass::InvalidCsvStructure,
295 ErrorClass::InvalidMarkdownStructure,
296 ErrorClass::InvalidHtmlStructure,
297 ErrorClass::InvalidTomlStructure,
298 ErrorClass::InternalInvariant,
299 ];
300 let mut codes: Vec<i32> = classes.iter().map(|c| c.exit_code()).collect();
301 let n = codes.len();
302 codes.sort_unstable();
303 codes.dedup();
304 assert_eq!(codes.len(), n, "exit codes must be unique");
305 assert_eq!(ErrorClass::InvalidContainer.exit_code(), 4);
306 }
307
308 #[test]
309 fn display_includes_class() {
310 let e = Error::invalid_container("bad magic");
311 assert_eq!(e.to_string(), "InvalidContainer: bad magic");
312 }
313}