use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ErrorCode {
InvalidPdf,
CorruptPdf,
PasswordProtected,
PageLimitExceeded,
FileTooLarge,
OcrRequired,
UnsupportedPdfFeature,
ParseTimeout,
MemoryLimitExceeded,
InternalError,
}
impl ErrorCode {
pub const ALL: [ErrorCode; 10] = [
ErrorCode::InvalidPdf,
ErrorCode::CorruptPdf,
ErrorCode::PasswordProtected,
ErrorCode::PageLimitExceeded,
ErrorCode::FileTooLarge,
ErrorCode::OcrRequired,
ErrorCode::UnsupportedPdfFeature,
ErrorCode::ParseTimeout,
ErrorCode::MemoryLimitExceeded,
ErrorCode::InternalError,
];
pub fn as_str(self) -> &'static str {
match self {
ErrorCode::InvalidPdf => "invalid_pdf",
ErrorCode::CorruptPdf => "corrupt_pdf",
ErrorCode::PasswordProtected => "password_protected",
ErrorCode::PageLimitExceeded => "page_limit_exceeded",
ErrorCode::FileTooLarge => "file_too_large",
ErrorCode::OcrRequired => "ocr_required",
ErrorCode::UnsupportedPdfFeature => "unsupported_pdf_feature",
ErrorCode::ParseTimeout => "parse_timeout",
ErrorCode::MemoryLimitExceeded => "memory_limit_exceeded",
ErrorCode::InternalError => "internal_error",
}
}
pub fn exit_code(self) -> i32 {
match self {
ErrorCode::InvalidPdf => 3,
ErrorCode::CorruptPdf => 4,
ErrorCode::PasswordProtected => 5,
ErrorCode::PageLimitExceeded => 6,
ErrorCode::FileTooLarge => 7,
ErrorCode::OcrRequired => 8,
ErrorCode::UnsupportedPdfFeature => 9,
ErrorCode::ParseTimeout => 10,
ErrorCode::MemoryLimitExceeded => 11,
ErrorCode::InternalError => 12,
}
}
}
impl core::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)]
#[error("{code}: {message}")]
pub struct EthosError {
pub code: ErrorCode,
pub message: String,
}
impl EthosError {
pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
EthosError {
code,
message: message.into(),
}
}
pub fn internal(message: impl Into<String>) -> Self {
EthosError::new(ErrorCode::InternalError, message)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exit_codes_are_dense_and_disjoint() {
let mut seen = std::collections::BTreeSet::new();
for code in ErrorCode::ALL {
assert!(seen.insert(code.exit_code()), "duplicate exit code");
}
assert_eq!(*seen.first().unwrap(), 3);
assert_eq!(*seen.last().unwrap(), 12);
assert_eq!(seen.len(), 10);
}
#[test]
fn wire_format_round_trips() {
for code in ErrorCode::ALL {
let json = serde_json::to_string(&code).unwrap();
assert_eq!(json, format!("\"{}\"", code.as_str()));
assert_eq!(serde_json::from_str::<ErrorCode>(&json).unwrap(), code);
}
}
}