vole_document/error.rs
1//! Typed errors with stable classes and documented CLI exit codes.
2//!
3//! Malformed data is never reported as an "internal invariant"; each failure
4//! class is distinct so callers and the CLI can react precisely.
5
6use core::fmt;
7
8/// Convenience alias for the crate's fallible operations.
9pub type Result<T> = core::result::Result<T, Error>;
10
11/// Stable error classes. These distinguish failure *kinds* rather than prose.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ErrorClass {
14 /// Host I/O failure (read/write/rename/fsync).
15 Io,
16 /// Command-line or API misuse.
17 Usage,
18 /// The container framing/header is malformed.
19 InvalidContainer,
20 /// The container declares a version this build does not implement.
21 UnsupportedVersion,
22 /// The container declares a mandatory feature this build does not support.
23 UnsupportedFeature,
24 /// A digest, length, or checksum disagreed with the declared value.
25 IntegrityMismatch,
26 /// A declared or configured resource bound was exceeded.
27 ResourceLimit,
28 /// The reconstruction graph is malformed (cycles, bad references, gaps).
29 InvalidGraph,
30 /// An entropy model descriptor is malformed.
31 InvalidModel,
32 /// An entropy channel failed to decode.
33 EntropyDecode,
34 /// A PDF physical/lexical structure error (Phase 3+).
35 InvalidPdfStructure,
36 /// An exact codec-replay candidate failed (Phase 6+).
37 CodecReplay,
38 /// A referenced external store object is missing (Phase 9+).
39 MissingExternalObject,
40 /// The coverage certificate is invalid (gaps or overlapping authorities).
41 CoverageViolation,
42 /// Materialization completed but did not equal the source.
43 ReconstructionMismatch,
44 /// The operation was cooperatively cancelled.
45 Cancelled,
46 /// A ZIP container structure error (Phase 12+): malformed or contradictory
47 /// local/central/ZIP64 records, an ambiguous byte cover, a broken descriptor,
48 /// multi-disk layout, or a hostile member name.
49 InvalidZipStructure,
50 /// An XML part structure error (Phase 12+, reserved): malformed or forbidden
51 /// constructs (DOCTYPE/XXE, encoding tricks) in a package part.
52 InvalidXmlStructure,
53 /// An OPC/OCF package structure error (Phase 12+, reserved): a missing or
54 /// ambiguous main part, content-type abuse, `mimetype` trick, or an ambiguous
55 /// part-name identity.
56 InvalidPackageStructure,
57 /// A JSON structured-tree structure error (Phase 21.5+): a malformed token,
58 /// an unterminated string/container, a bad escape, or a trailing byte after
59 /// the single top-level value.
60 InvalidJsonStructure,
61 /// A YAML structured-tree structure error (Phase 21.6+): a malformed or
62 /// unsupported construct (a bad indentation, an unterminated quoted scalar, a
63 /// declined directive/explicit key/flow-context block scalar).
64 InvalidYamlStructure,
65 /// A CSV/TSV tabular structure error (Phase 21.7+): an unterminated quoted
66 /// field, a byte after a closing quote that is neither a delimiter nor an end
67 /// of record, or an input that is not a table under either delimiter.
68 InvalidCsvStructure,
69 /// A Markdown prose structure error (Phase 21.8+): a malformed or declined
70 /// construct (an unterminated fenced code block close, a bad reference
71 /// definition, or an input that carries no structural mark).
72 InvalidMarkdownStructure,
73 /// An HTML document structure error (Phase 21.10+): a forbidden construct
74 /// (a DOCTYPE with an internal subset), a non-UTF-8/UTF-16/NUL byte string, or
75 /// an input that carries no HTML structure. Malformed HTML is otherwise
76 /// **recovered** by the adapter, never a panic.
77 InvalidHtmlStructure,
78 /// A TOML structure error (Phase 21.11+): a malformed token, an unterminated
79 /// string/array/inline table, an invalid number or escape, or a violation of
80 /// TOML's duplicate-key/redefinition rules (which this adapter **enforces**).
81 InvalidTomlStructure,
82 /// A JSONL/NDJSON structure error (Phase 21.12+): a non-blank line that is not
83 /// exactly one JSON value, too few record lines to distinguish the source from a
84 /// single JSON value, or a non-newline-separated bag of JSON values.
85 InvalidJsonlStructure,
86 /// An EML/MIME message structure error (Phase 21.13+): no RFC 5322 header block,
87 /// a malformed header line, a `multipart/*` without a boundary, or an invalid
88 /// quoted-printable/base64 escape.
89 InvalidEmlStructure,
90 /// An Apache Parquet structure error (Phase 21.14+): missing/inconsistent `PAR1`
91 /// magic, an inconsistent footer length, a truncated Thrift-Compact footer, a
92 /// page or column-chunk span outside the file, or a value count that disagrees
93 /// with the page layout.
94 InvalidParquetStructure,
95 /// An Apache Arrow IPC structure error (Phase 21.16+): missing/inconsistent
96 /// `ARROW1` magic, a truncated or out-of-bounds Flatbuffers metadata table, an
97 /// invalid message framing, or a buffer span outside the message body.
98 InvalidArrowStructure,
99 /// A JSON5/JSONC structure error (Phase 21.17.1+): a malformed token, an
100 /// unterminated string/comment/container, a bad escape or identifier, or a
101 /// trailing byte after the single top-level value.
102 InvalidJson5Structure,
103 /// A CBOR structure error (Phase 21.18+): a malformed head (reserved additional
104 /// information, a non-minimal simple value), a truncated item, trailing bytes
105 /// after the single top-level item, an unterminated indefinite-length item, a
106 /// map key with no value, or a text string that is not valid UTF-8.
107 InvalidCborStructure,
108 /// A MessagePack structure error (Phase 21.19+): a malformed head (the never-used
109 /// `0xc1` byte), a truncated item, trailing bytes after the single top-level item,
110 /// a map key with no value, an over-long declared length, or a `str` that is not
111 /// valid UTF-8.
112 InvalidMsgpackStructure,
113 /// A config-family structure error (Phase 21.20+): a line that is not a valid
114 /// INI/`.env`/`.properties` construct under the detected dialect, an unterminated
115 /// quoted value, or a source that carries no config-family dialect signal at all
116 /// (so it stays `Opaque`).
117 InvalidConfigStructure,
118 /// An RSS/Atom feed structure error (Phase 21.21+): a source that is not
119 /// well-formed XML, an `<rss>` root with no `<channel>`, an Atom `<feed>` whose
120 /// namespace is not the Atom URI or which carries no `<entry>`, or a root that is
121 /// not an RSS/Atom feed (so it stays `Xml`/`Opaque`).
122 InvalidFeedStructure,
123 /// A GeoJSON structure error (Phase 21.22+): a source that is not a single JSON
124 /// value, a root that is not an object with a string `"type"` member, a `"type"`
125 /// that is not one of the nine GeoJSON type names, or a type-consistent object
126 /// whose shape is not consistent (a geometry with no array `coordinates`/`geometries`,
127 /// a `Feature` with no `geometry`/`properties`, a `FeatureCollection` with no array
128 /// `features`) — so it stays `Json`/`Opaque` rather than being claimed.
129 InvalidGeojsonStructure,
130 /// A KML/GPX geospatial structure error (Phase 21.23+): a source that is not
131 /// well-formed XML, a `<kml>` root not in the KML namespace or with no
132 /// `Document`/`Folder`/`Placemark` child, a `<gpx>` root not in the GPX namespace
133 /// or with no `metadata`/`wpt`/`rte`/`trk` child, or a root that is not a KML/GPX
134 /// document (so it stays `Xml`/`Opaque`).
135 InvalidGisStructure,
136 /// A Jupyter notebook structure error (Phase 21.24+): a source that is not a single
137 /// JSON value, a root that is not an object with a plain non-negative integer-literal
138 /// `nbformat` (≥ 1) and an array `cells`, a cell that is not an object with a string
139 /// `cell_type`, a present-but-malformed recognized cell field (a `source` that is not
140 /// a string or array of strings, an `execution_count` that is neither a number nor
141 /// `null`, a non-object `metadata`/`attachments`, an `outputs` that is not an array of
142 /// objects with a string `output_type`), or a non-object root `metadata` — so it stays
143 /// `Json`/`Opaque` rather than being claimed.
144 InvalidNotebookStructure,
145 /// A fixed-width structure error (Phase 21.25+): a source that carries no strong
146 /// fixed-width signal (fewer than the minimum sampled records, records that do not
147 /// share an identical byte width, fewer than two non-empty columns, or an interior
148 /// column gap narrower than the minimum), or a document whose later records
149 /// disagree with the sampled layout — so it stays `Opaque` rather than being
150 /// claimed.
151 InvalidFixedWidthStructure,
152 /// A reStructuredText structure error (Phase 21.26.1+): a source that carries no
153 /// reST-specific signal (an explicit markup start, a grid/simple table, a field
154 /// list, or an admissible section adornment), a malformed directive/marker, or an
155 /// input that exceeds a cap — so it stays `Opaque` rather than being claimed.
156 InvalidRstStructure,
157 /// An AsciiDoc structure error (Phase 21.26.2+): a source that carries no
158 /// AsciiDoc-specific signal (a document title followed by a block, a `==`+ section,
159 /// a `|===` table, a complete delimited block, or a block attribute line followed by
160 /// a block), a malformed attribute/list/macro marker, or an input that exceeds a cap
161 /// — so it stays `Opaque` rather than being claimed.
162 InvalidAsciidocStructure,
163 /// An MDX structure error (Phase 21.26.3+): a source that carries no MDX-specific
164 /// signal (a top-level ESM statement, a JSX component/fragment/attribute, or a
165 /// whole-line block expression), an unbalanced/ambiguous brace in a non-code
166 /// position, a malformed JSX/ESM construct, or an input that exceeds a cap — so it
167 /// stays `Opaque` rather than being claimed.
168 InvalidMdxStructure,
169 /// An MHTML structure error (Phase 21.27): a source that is not a well-formed MIME
170 /// message, a root that is not `multipart/related`, a `multipart/related` document
171 /// that carries no MHTML-specific signal (`Snapshot-Content-Location`/`Content-Base`,
172 /// or a `From`+`Subject` envelope with a `text/html` part), a root part that cannot
173 /// be decoded/parsed as HTML, or an input that exceeds a cap — so it stays
174 /// `Eml`/`Html`/`Opaque` rather than being claimed.
175 InvalidMhtmlStructure,
176 /// A syslog / log-stream structure error (Phase 21.28): a source that is not a log
177 /// stream (fewer than two non-blank record lines, or a non-blank line that matches
178 /// no recognized dialect — RFC 5424 syslog, RFC 3164 (BSD) syslog, or a generic
179 /// application log line), or an input that exceeds a cap — so it stays `Opaque`
180 /// rather than being claimed.
181 InvalidLogstreamStructure,
182 /// A package-metadata structure error (Phase 21.29): a JSON/TOML source that is not
183 /// a recognized package-manifest shape (an npm `package.json`/`package-lock.json`, a
184 /// Cargo `Cargo.toml`/`Cargo.lock`, or a Python `pyproject.toml`), a malformed
185 /// manifest, or an input that exceeds a cap — so it stays `Json`/`Toml`/`Opaque`
186 /// rather than being claimed.
187 InvalidPkgmetaStructure,
188 /// An API-specification structure error (Phase 21.30): a JSON source that is not a
189 /// recognized API spec (a JSON Schema, an OpenAPI 3.x, a Swagger 2.0, or an
190 /// AsyncAPI document), a root object carrying no strong marker, a malformed source,
191 /// or an input that exceeds a cap — so it stays `Json`/`Opaque` rather than being
192 /// claimed.
193 InvalidApispecStructure,
194 /// A bibliography structure error (Phase 21.31): a source that is not a recognized
195 /// bibliography (a BibTeX entry sequence, an RIS `TY`/`ER` entry sequence, or a
196 /// CSL-JSON item array/object carrying a string `id` and a string `type`), a
197 /// malformed entry/field, or an input that exceeds a cap — so it stays
198 /// `Json`/`Opaque` rather than being claimed.
199 InvalidBibStructure,
200 /// An RTF (Rich Text Format) structure error (Phase 21.32): a source that begins
201 /// with `{\rtf<digit>` but is not well-formed under the bounded group-brace
202 /// grammar (an unbalanced brace, a trailing backslash, an over-long control word,
203 /// or trailing bytes after the root group) — so it stays `Opaque` rather than being
204 /// claimed as a derived model. Detection and the exact leaf are unaffected.
205 InvalidRtfStructure,
206 /// A generic archive-container structure error (Phase 21.33): a source that is
207 /// neither a valid ZIP (magic + EOCD + coherent central directory) nor a valid
208 /// TAR (a strong header whose checksum matches), or a container whose TAR
209 /// member is truncated / has a bad checksum / has a malformed PAX record — so
210 /// it stays `Opaque` rather than being claimed as a derived model. Detection
211 /// and the whole-source exact leaf are unaffected.
212 InvalidContainerStructure,
213 /// A scientific-container structure error (Phase 21.34): a NetCDF classic
214 /// (CDF-1/CDF-2) or HDF5 source that is structurally malformed — a bad tag, an
215 /// out-of-bounds declared offset, a truncated header/message, an unsatisfied
216 /// object-header message count, or a bad local heap / B-tree / symbol table — so
217 /// it stays `Opaque` rather than being claimed as a derived model. Detection and
218 /// the whole-source exact leaf are unaffected.
219 InvalidHdfStructure,
220 /// A CFBF/OLE2 structure error (Phase 21.35): a compound-file source that is
221 /// structurally malformed — an inconsistent header, a bad sector shift, an
222 /// out-of-range sector ID, a cyclic/overlapping FAT or MiniFAT chain, an invalid
223 /// directory object type, a truncated stream, or a header/sector-size mismatch —
224 /// so it stays `Opaque` rather than being claimed as a derived model. Detection
225 /// and the whole-source exact leaf are unaffected.
226 InvalidCfbfStructure,
227 /// A bug in this implementation; never a description of malformed input.
228 InternalInvariant,
229}
230
231impl ErrorClass {
232 /// Stable, documented CLI exit code for this class.
233 pub const fn exit_code(self) -> i32 {
234 match self {
235 ErrorClass::Io => 3,
236 ErrorClass::Usage => 2,
237 ErrorClass::InvalidContainer => 4,
238 ErrorClass::UnsupportedVersion => 5,
239 ErrorClass::UnsupportedFeature => 6,
240 ErrorClass::IntegrityMismatch => 7,
241 ErrorClass::ResourceLimit => 8,
242 ErrorClass::InvalidGraph => 9,
243 ErrorClass::InvalidModel => 10,
244 ErrorClass::EntropyDecode => 11,
245 ErrorClass::InvalidPdfStructure => 12,
246 ErrorClass::CodecReplay => 13,
247 ErrorClass::MissingExternalObject => 14,
248 ErrorClass::CoverageViolation => 15,
249 ErrorClass::ReconstructionMismatch => 16,
250 ErrorClass::Cancelled => 17,
251 ErrorClass::InvalidZipStructure => 18,
252 ErrorClass::InvalidXmlStructure => 19,
253 ErrorClass::InvalidPackageStructure => 20,
254 ErrorClass::InvalidJsonStructure => 21,
255 ErrorClass::InvalidYamlStructure => 22,
256 ErrorClass::InvalidCsvStructure => 23,
257 ErrorClass::InvalidMarkdownStructure => 24,
258 ErrorClass::InvalidHtmlStructure => 25,
259 ErrorClass::InvalidTomlStructure => 26,
260 ErrorClass::InvalidJsonlStructure => 27,
261 ErrorClass::InvalidEmlStructure => 28,
262 ErrorClass::InvalidParquetStructure => 29,
263 ErrorClass::InvalidArrowStructure => 30,
264 ErrorClass::InvalidJson5Structure => 31,
265 ErrorClass::InvalidCborStructure => 32,
266 ErrorClass::InvalidMsgpackStructure => 33,
267 ErrorClass::InvalidConfigStructure => 34,
268 ErrorClass::InvalidFeedStructure => 35,
269 ErrorClass::InvalidGeojsonStructure => 36,
270 ErrorClass::InvalidGisStructure => 37,
271 ErrorClass::InvalidNotebookStructure => 38,
272 ErrorClass::InvalidFixedWidthStructure => 39,
273 ErrorClass::InvalidRstStructure => 40,
274 ErrorClass::InvalidAsciidocStructure => 41,
275 ErrorClass::InvalidMdxStructure => 42,
276 ErrorClass::InvalidMhtmlStructure => 43,
277 ErrorClass::InvalidLogstreamStructure => 44,
278 ErrorClass::InvalidPkgmetaStructure => 45,
279 ErrorClass::InvalidApispecStructure => 46,
280 ErrorClass::InvalidBibStructure => 47,
281 ErrorClass::InvalidRtfStructure => 48,
282 ErrorClass::InvalidContainerStructure => 49,
283 ErrorClass::InvalidHdfStructure => 50,
284 ErrorClass::InvalidCfbfStructure => 51,
285 ErrorClass::InternalInvariant => 70,
286 }
287 }
288
289 /// Short stable identifier used in receipts and machine-readable output.
290 pub const fn as_str(self) -> &'static str {
291 match self {
292 ErrorClass::Io => "Io",
293 ErrorClass::Usage => "Usage",
294 ErrorClass::InvalidContainer => "InvalidContainer",
295 ErrorClass::UnsupportedVersion => "UnsupportedVersion",
296 ErrorClass::UnsupportedFeature => "UnsupportedFeature",
297 ErrorClass::IntegrityMismatch => "IntegrityMismatch",
298 ErrorClass::ResourceLimit => "ResourceLimit",
299 ErrorClass::InvalidGraph => "InvalidGraph",
300 ErrorClass::InvalidModel => "InvalidModel",
301 ErrorClass::EntropyDecode => "EntropyDecode",
302 ErrorClass::InvalidPdfStructure => "InvalidPdfStructure",
303 ErrorClass::CodecReplay => "CodecReplay",
304 ErrorClass::MissingExternalObject => "MissingExternalObject",
305 ErrorClass::CoverageViolation => "CoverageViolation",
306 ErrorClass::ReconstructionMismatch => "ReconstructionMismatch",
307 ErrorClass::Cancelled => "Cancelled",
308 ErrorClass::InvalidZipStructure => "InvalidZipStructure",
309 ErrorClass::InvalidXmlStructure => "InvalidXmlStructure",
310 ErrorClass::InvalidPackageStructure => "InvalidPackageStructure",
311 ErrorClass::InvalidJsonStructure => "InvalidJsonStructure",
312 ErrorClass::InvalidYamlStructure => "InvalidYamlStructure",
313 ErrorClass::InvalidCsvStructure => "InvalidCsvStructure",
314 ErrorClass::InvalidMarkdownStructure => "InvalidMarkdownStructure",
315 ErrorClass::InvalidHtmlStructure => "InvalidHtmlStructure",
316 ErrorClass::InvalidTomlStructure => "InvalidTomlStructure",
317 ErrorClass::InvalidJsonlStructure => "InvalidJsonlStructure",
318 ErrorClass::InvalidEmlStructure => "InvalidEmlStructure",
319 ErrorClass::InvalidParquetStructure => "InvalidParquetStructure",
320 ErrorClass::InvalidArrowStructure => "InvalidArrowStructure",
321 ErrorClass::InvalidJson5Structure => "InvalidJson5Structure",
322 ErrorClass::InvalidCborStructure => "InvalidCborStructure",
323 ErrorClass::InvalidMsgpackStructure => "InvalidMsgpackStructure",
324 ErrorClass::InvalidConfigStructure => "InvalidConfigStructure",
325 ErrorClass::InvalidFeedStructure => "InvalidFeedStructure",
326 ErrorClass::InvalidGeojsonStructure => "InvalidGeojsonStructure",
327 ErrorClass::InvalidGisStructure => "InvalidGisStructure",
328 ErrorClass::InvalidNotebookStructure => "InvalidNotebookStructure",
329 ErrorClass::InvalidFixedWidthStructure => "InvalidFixedWidthStructure",
330 ErrorClass::InvalidRstStructure => "InvalidRstStructure",
331 ErrorClass::InvalidAsciidocStructure => "InvalidAsciidocStructure",
332 ErrorClass::InvalidMdxStructure => "InvalidMdxStructure",
333 ErrorClass::InvalidMhtmlStructure => "InvalidMhtmlStructure",
334 ErrorClass::InvalidLogstreamStructure => "InvalidLogstreamStructure",
335 ErrorClass::InvalidPkgmetaStructure => "InvalidPkgmetaStructure",
336 ErrorClass::InvalidApispecStructure => "InvalidApispecStructure",
337 ErrorClass::InvalidBibStructure => "InvalidBibStructure",
338 ErrorClass::InvalidRtfStructure => "InvalidRtfStructure",
339 ErrorClass::InvalidContainerStructure => "InvalidContainerStructure",
340 ErrorClass::InvalidHdfStructure => "InvalidHdfStructure",
341 ErrorClass::InvalidCfbfStructure => "InvalidCfbfStructure",
342 ErrorClass::InternalInvariant => "InternalInvariant",
343 }
344 }
345}
346
347/// A typed, classified error.
348#[derive(Debug, Clone)]
349pub struct Error {
350 class: ErrorClass,
351 message: String,
352}
353
354impl Error {
355 /// Construct an error of the given class with a human-readable message.
356 pub fn new(class: ErrorClass, message: impl Into<String>) -> Self {
357 Error {
358 class,
359 message: message.into(),
360 }
361 }
362
363 /// The failure class.
364 pub fn class(&self) -> ErrorClass {
365 self.class
366 }
367
368 /// The stable CLI exit code for this error's class.
369 pub fn exit_code(&self) -> i32 {
370 self.class.exit_code()
371 }
372
373 /// The human-readable message.
374 pub fn message(&self) -> &str {
375 &self.message
376 }
377}
378
379impl fmt::Display for Error {
380 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381 write!(f, "{}: {}", self.class.as_str(), self.message)
382 }
383}
384
385impl std::error::Error for Error {}
386
387impl From<std::io::Error> for Error {
388 fn from(e: std::io::Error) -> Self {
389 Error::new(ErrorClass::Io, e.to_string())
390 }
391}
392
393macro_rules! ctor {
394 ($name:ident, $class:ident) => {
395 /// Construct an error of this class.
396 pub fn $name(message: impl Into<String>) -> Self {
397 Error::new(ErrorClass::$class, message)
398 }
399 };
400}
401
402impl Error {
403 ctor!(io, Io);
404 ctor!(usage, Usage);
405 ctor!(invalid_container, InvalidContainer);
406 ctor!(unsupported_version, UnsupportedVersion);
407 ctor!(unsupported_feature, UnsupportedFeature);
408 ctor!(integrity_mismatch, IntegrityMismatch);
409 ctor!(resource_limit, ResourceLimit);
410 ctor!(invalid_graph, InvalidGraph);
411 ctor!(invalid_model, InvalidModel);
412 ctor!(entropy_decode, EntropyDecode);
413 ctor!(invalid_pdf_structure, InvalidPdfStructure);
414 ctor!(codec_replay, CodecReplay);
415 ctor!(missing_external_object, MissingExternalObject);
416 ctor!(coverage_violation, CoverageViolation);
417 ctor!(reconstruction_mismatch, ReconstructionMismatch);
418 ctor!(cancelled, Cancelled);
419 ctor!(invalid_zip_structure, InvalidZipStructure);
420 ctor!(invalid_xml_structure, InvalidXmlStructure);
421 ctor!(invalid_package_structure, InvalidPackageStructure);
422 ctor!(invalid_json_structure, InvalidJsonStructure);
423 ctor!(invalid_yaml_structure, InvalidYamlStructure);
424 ctor!(invalid_csv_structure, InvalidCsvStructure);
425 ctor!(invalid_markdown_structure, InvalidMarkdownStructure);
426 ctor!(invalid_html_structure, InvalidHtmlStructure);
427 ctor!(invalid_toml_structure, InvalidTomlStructure);
428 ctor!(invalid_jsonl_structure, InvalidJsonlStructure);
429 ctor!(invalid_eml_structure, InvalidEmlStructure);
430 ctor!(invalid_parquet_structure, InvalidParquetStructure);
431 ctor!(invalid_arrow_structure, InvalidArrowStructure);
432 ctor!(invalid_json5_structure, InvalidJson5Structure);
433 ctor!(invalid_cbor_structure, InvalidCborStructure);
434 ctor!(invalid_msgpack_structure, InvalidMsgpackStructure);
435 ctor!(invalid_config_structure, InvalidConfigStructure);
436 ctor!(invalid_feed_structure, InvalidFeedStructure);
437 ctor!(invalid_geojson_structure, InvalidGeojsonStructure);
438 ctor!(invalid_gis_structure, InvalidGisStructure);
439 ctor!(invalid_notebook_structure, InvalidNotebookStructure);
440 ctor!(invalid_fixedwidth_structure, InvalidFixedWidthStructure);
441 ctor!(invalid_rst_structure, InvalidRstStructure);
442 ctor!(invalid_asciidoc_structure, InvalidAsciidocStructure);
443 ctor!(invalid_mdx_structure, InvalidMdxStructure);
444 ctor!(invalid_mhtml_structure, InvalidMhtmlStructure);
445 ctor!(invalid_logstream_structure, InvalidLogstreamStructure);
446 ctor!(invalid_pkgmeta_structure, InvalidPkgmetaStructure);
447 ctor!(invalid_apispec_structure, InvalidApispecStructure);
448 ctor!(invalid_bib_structure, InvalidBibStructure);
449 ctor!(invalid_rtf_structure, InvalidRtfStructure);
450 ctor!(invalid_container_structure, InvalidContainerStructure);
451 ctor!(invalid_hdf_structure, InvalidHdfStructure);
452 ctor!(invalid_cfbf_structure, InvalidCfbfStructure);
453 ctor!(internal_invariant, InternalInvariant);
454}
455
456#[cfg(test)]
457mod tests {
458 use super::*;
459
460 #[test]
461 fn exit_codes_are_distinct_and_stable() {
462 // A representative set; guards accidental collisions.
463 let classes = [
464 ErrorClass::Io,
465 ErrorClass::Usage,
466 ErrorClass::InvalidContainer,
467 ErrorClass::UnsupportedVersion,
468 ErrorClass::UnsupportedFeature,
469 ErrorClass::IntegrityMismatch,
470 ErrorClass::ResourceLimit,
471 ErrorClass::InvalidGraph,
472 ErrorClass::InvalidModel,
473 ErrorClass::EntropyDecode,
474 ErrorClass::InvalidPdfStructure,
475 ErrorClass::CodecReplay,
476 ErrorClass::MissingExternalObject,
477 ErrorClass::CoverageViolation,
478 ErrorClass::ReconstructionMismatch,
479 ErrorClass::Cancelled,
480 ErrorClass::InvalidZipStructure,
481 ErrorClass::InvalidXmlStructure,
482 ErrorClass::InvalidPackageStructure,
483 ErrorClass::InvalidJsonStructure,
484 ErrorClass::InvalidYamlStructure,
485 ErrorClass::InvalidCsvStructure,
486 ErrorClass::InvalidMarkdownStructure,
487 ErrorClass::InvalidHtmlStructure,
488 ErrorClass::InvalidTomlStructure,
489 ErrorClass::InternalInvariant,
490 ];
491 let mut codes: Vec<i32> = classes.iter().map(|c| c.exit_code()).collect();
492 let n = codes.len();
493 codes.sort_unstable();
494 codes.dedup();
495 assert_eq!(codes.len(), n, "exit codes must be unique");
496 assert_eq!(ErrorClass::InvalidContainer.exit_code(), 4);
497 }
498
499 #[test]
500 fn display_includes_class() {
501 let e = Error::invalid_container("bad magic");
502 assert_eq!(e.to_string(), "InvalidContainer: bad magic");
503 }
504}