use core::fmt;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorClass {
Io,
Usage,
InvalidContainer,
UnsupportedVersion,
UnsupportedFeature,
IntegrityMismatch,
ResourceLimit,
InvalidGraph,
InvalidModel,
EntropyDecode,
InvalidPdfStructure,
CodecReplay,
MissingExternalObject,
CoverageViolation,
ReconstructionMismatch,
Cancelled,
InternalInvariant,
}
impl ErrorClass {
pub const fn exit_code(self) -> i32 {
match self {
ErrorClass::Io => 3,
ErrorClass::Usage => 2,
ErrorClass::InvalidContainer => 4,
ErrorClass::UnsupportedVersion => 5,
ErrorClass::UnsupportedFeature => 6,
ErrorClass::IntegrityMismatch => 7,
ErrorClass::ResourceLimit => 8,
ErrorClass::InvalidGraph => 9,
ErrorClass::InvalidModel => 10,
ErrorClass::EntropyDecode => 11,
ErrorClass::InvalidPdfStructure => 12,
ErrorClass::CodecReplay => 13,
ErrorClass::MissingExternalObject => 14,
ErrorClass::CoverageViolation => 15,
ErrorClass::ReconstructionMismatch => 16,
ErrorClass::Cancelled => 17,
ErrorClass::InternalInvariant => 70,
}
}
pub const fn as_str(self) -> &'static str {
match self {
ErrorClass::Io => "Io",
ErrorClass::Usage => "Usage",
ErrorClass::InvalidContainer => "InvalidContainer",
ErrorClass::UnsupportedVersion => "UnsupportedVersion",
ErrorClass::UnsupportedFeature => "UnsupportedFeature",
ErrorClass::IntegrityMismatch => "IntegrityMismatch",
ErrorClass::ResourceLimit => "ResourceLimit",
ErrorClass::InvalidGraph => "InvalidGraph",
ErrorClass::InvalidModel => "InvalidModel",
ErrorClass::EntropyDecode => "EntropyDecode",
ErrorClass::InvalidPdfStructure => "InvalidPdfStructure",
ErrorClass::CodecReplay => "CodecReplay",
ErrorClass::MissingExternalObject => "MissingExternalObject",
ErrorClass::CoverageViolation => "CoverageViolation",
ErrorClass::ReconstructionMismatch => "ReconstructionMismatch",
ErrorClass::Cancelled => "Cancelled",
ErrorClass::InternalInvariant => "InternalInvariant",
}
}
}
#[derive(Debug, Clone)]
pub struct Error {
class: ErrorClass,
message: String,
}
impl Error {
pub fn new(class: ErrorClass, message: impl Into<String>) -> Self {
Error {
class,
message: message.into(),
}
}
pub fn class(&self) -> ErrorClass {
self.class
}
pub fn exit_code(&self) -> i32 {
self.class.exit_code()
}
pub fn message(&self) -> &str {
&self.message
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.class.as_str(), self.message)
}
}
impl std::error::Error for Error {}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::new(ErrorClass::Io, e.to_string())
}
}
macro_rules! ctor {
($name:ident, $class:ident) => {
pub fn $name(message: impl Into<String>) -> Self {
Error::new(ErrorClass::$class, message)
}
};
}
impl Error {
ctor!(io, Io);
ctor!(usage, Usage);
ctor!(invalid_container, InvalidContainer);
ctor!(unsupported_version, UnsupportedVersion);
ctor!(unsupported_feature, UnsupportedFeature);
ctor!(integrity_mismatch, IntegrityMismatch);
ctor!(resource_limit, ResourceLimit);
ctor!(invalid_graph, InvalidGraph);
ctor!(invalid_model, InvalidModel);
ctor!(entropy_decode, EntropyDecode);
ctor!(invalid_pdf_structure, InvalidPdfStructure);
ctor!(codec_replay, CodecReplay);
ctor!(missing_external_object, MissingExternalObject);
ctor!(coverage_violation, CoverageViolation);
ctor!(reconstruction_mismatch, ReconstructionMismatch);
ctor!(cancelled, Cancelled);
ctor!(internal_invariant, InternalInvariant);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exit_codes_are_distinct_and_stable() {
let classes = [
ErrorClass::Io,
ErrorClass::Usage,
ErrorClass::InvalidContainer,
ErrorClass::UnsupportedVersion,
ErrorClass::UnsupportedFeature,
ErrorClass::IntegrityMismatch,
ErrorClass::ResourceLimit,
ErrorClass::InvalidGraph,
ErrorClass::InvalidModel,
ErrorClass::EntropyDecode,
ErrorClass::InvalidPdfStructure,
ErrorClass::CodecReplay,
ErrorClass::MissingExternalObject,
ErrorClass::CoverageViolation,
ErrorClass::ReconstructionMismatch,
ErrorClass::Cancelled,
ErrorClass::InternalInvariant,
];
let mut codes: Vec<i32> = classes.iter().map(|c| c.exit_code()).collect();
let n = codes.len();
codes.sort_unstable();
codes.dedup();
assert_eq!(codes.len(), n, "exit codes must be unique");
assert_eq!(ErrorClass::InvalidContainer.exit_code(), 4);
}
#[test]
fn display_includes_class() {
let e = Error::invalid_container("bad magic");
assert_eq!(e.to_string(), "InvalidContainer: bad magic");
}
}