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    InternalError,
31}
32
33impl ErrorCategory {
34    /// Stable catalog string (snake_case, as in RFC-004/RFC-005).
35    pub fn as_str(&self) -> &'static str {
36        match self {
37            ErrorCategory::SourceMissing => "source_missing",
38            ErrorCategory::PermissionDenied => "permission_denied",
39            ErrorCategory::PathCanonicalizationFailed => "path_canonicalization_failed",
40            ErrorCategory::SymlinkPolicyBlocked => "symlink_policy_blocked",
41            ErrorCategory::FileTooLarge => "file_too_large",
42            ErrorCategory::UnsupportedType => "unsupported_type",
43            ErrorCategory::UnsupportedFormat => "unsupported_format",
44            ErrorCategory::EncodingError => "encoding_error",
45            ErrorCategory::ParserError => "parser_error",
46            ErrorCategory::EncryptedDocument => "encrypted_document",
47            ErrorCategory::FileChangedDuringRead => "file_changed_during_read",
48            ErrorCategory::ReadError => "read_error",
49            ErrorCategory::HashError => "hash_error",
50            ErrorCategory::Timeout => "timeout",
51            ErrorCategory::OutOfMemory => "out_of_memory",
52            ErrorCategory::Canceled => "canceled",
53            ErrorCategory::ModelUnavailable => "model_unavailable",
54            ErrorCategory::InternalError => "internal_error",
55        }
56    }
57}
58
59/// Top-level orbok error.
60///
61/// Messages intentionally avoid document contents; paths appear only
62/// where required for actionability (NFR-014 log hygiene).
63#[derive(Debug, Error)]
64pub enum OrbokError {
65    #[error("database error: {0}")]
66    Database(String),
67
68    #[error("migration failed at version {version}: {message}")]
69    MigrationFailed { version: i64, message: String },
70
71    #[error("path is outside all active sources")]
72    PathOutsideSources,
73
74    #[error("path canonicalization failed: {0}")]
75    PathCanonicalization(String),
76
77    #[error("blocked by source policy: {0}")]
78    PolicyBlocked(&'static str),
79
80    #[error("source not found")]
81    SourceNotFound,
82
83    #[error("file not found in catalog")]
84    FileNotFound,
85
86    #[error("cleanup plan would touch persistent catalog data")]
87    CleanupWouldTouchPersistentData,
88
89    #[error("cache engine error: {0}")]
90    Cache(String),
91
92    #[error("extraction failed: {category:?}")]
93    Extraction {
94        category: ErrorCategory,
95        message: String,
96    },
97
98    #[error("invalid value in catalog column {column}: {value}")]
99    InvalidCatalogValue { column: &'static str, value: String },
100
101    #[error("I/O error: {0}")]
102    Io(#[from] std::io::Error),
103
104    #[error("operation canceled")]
105    Canceled,
106}
107
108/// Convenience result alias.
109pub type OrbokResult<T> = Result<T, OrbokError>;