Skip to main content

orbok_core/
error.rs

1//! Application error types.
2//!
3//! Backend crates return typed errors; the UI maps them to i18n message
4//! keys (RFC-031 §6 rule 2). Error categories follow the failure
5//! taxonomies of RFC-004 §16 and RFC-005 §13.
6
7use thiserror::Error;
8
9/// Stable error categories recorded in the catalog
10/// (`extraction_records.error_category`, `index_jobs.error_category`).
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ErrorCategory {
13    SourceMissing,
14    PermissionDenied,
15    PathCanonicalizationFailed,
16    SymlinkPolicyBlocked,
17    FileTooLarge,
18    UnsupportedType,
19    UnsupportedFormat,
20    EncodingError,
21    ParserError,
22    EncryptedDocument,
23    FileChangedDuringRead,
24    ReadError,
25    HashError,
26    Timeout,
27    OutOfMemory,
28    Canceled,
29    ModelUnavailable,
30    ParserPanic,
31    InternalError,
32}
33
34impl ErrorCategory {
35    /// Stable catalog string (snake_case, as in RFC-004/RFC-005).
36    pub fn as_str(&self) -> &'static str {
37        match self {
38            ErrorCategory::SourceMissing => "source_missing",
39            ErrorCategory::PermissionDenied => "permission_denied",
40            ErrorCategory::PathCanonicalizationFailed => "path_canonicalization_failed",
41            ErrorCategory::SymlinkPolicyBlocked => "symlink_policy_blocked",
42            ErrorCategory::FileTooLarge => "file_too_large",
43            ErrorCategory::UnsupportedType => "unsupported_type",
44            ErrorCategory::UnsupportedFormat => "unsupported_format",
45            ErrorCategory::EncodingError => "encoding_error",
46            ErrorCategory::ParserError => "parser_error",
47            ErrorCategory::EncryptedDocument => "encrypted_document",
48            ErrorCategory::FileChangedDuringRead => "file_changed_during_read",
49            ErrorCategory::ReadError => "read_error",
50            ErrorCategory::HashError => "hash_error",
51            ErrorCategory::Timeout => "timeout",
52            ErrorCategory::OutOfMemory => "out_of_memory",
53            ErrorCategory::Canceled => "canceled",
54            ErrorCategory::ModelUnavailable => "model_unavailable",
55            ErrorCategory::ParserPanic => "parser_panic",
56            ErrorCategory::InternalError => "internal_error",
57        }
58    }
59}
60
61/// Top-level orbok error.
62///
63/// Messages intentionally avoid document contents; paths appear only
64/// where required for actionability (NFR-014 log hygiene).
65#[derive(Debug, Error)]
66pub enum OrbokError {
67    #[error("database error: {0}")]
68    Database(String),
69
70    #[error("migration failed at version {version}: {message}")]
71    MigrationFailed { version: i64, message: String },
72
73    #[error("path is outside all active sources")]
74    PathOutsideSources,
75
76    #[error("path canonicalization failed: {0}")]
77    PathCanonicalization(String),
78
79    #[error("blocked by source policy: {0}")]
80    PolicyBlocked(&'static str),
81
82    #[error("source not found")]
83    SourceNotFound,
84
85    #[error("file not found in catalog")]
86    FileNotFound,
87
88    #[error("cleanup plan would touch persistent catalog data")]
89    CleanupWouldTouchPersistentData,
90
91    #[error("cache engine error: {0}")]
92    Cache(String),
93
94    #[error("extraction failed: {category:?}")]
95    Extraction {
96        category: ErrorCategory,
97        message: String,
98    },
99
100    #[error("invalid value in catalog column {column}: {value}")]
101    InvalidCatalogValue { column: &'static str, value: String },
102
103    #[error("I/O error: {0}")]
104    Io(#[from] std::io::Error),
105
106    #[error("operation canceled")]
107    Canceled,
108}
109
110/// Convenience result alias.
111pub type OrbokResult<T> = Result<T, OrbokError>;