infiniloom_engine/
error.rs

1//! Unified error types for Infiniloom
2//!
3//! This module provides a top-level error type that wraps all domain-specific
4//! errors for convenient error handling across the library.
5
6use thiserror::Error;
7
8use crate::config::ConfigError;
9use crate::git::GitError;
10use crate::incremental::CacheError;
11use crate::parser::ParserError;
12use crate::remote::RemoteError;
13use crate::semantic::SemanticError;
14
15/// Unified error type for all Infiniloom operations
16#[derive(Debug, Error)]
17pub enum InfiniloomError {
18    /// Parser-related errors
19    #[error("Parse error: {0}")]
20    Parser(#[from] ParserError),
21
22    /// Git operation errors
23    #[error("Git error: {0}")]
24    Git(#[from] GitError),
25
26    /// Remote repository errors
27    #[error("Remote error: {0}")]
28    Remote(#[from] RemoteError),
29
30    /// Configuration errors
31    #[error("Config error: {0}")]
32    Config(#[from] ConfigError),
33
34    /// Cache/incremental scanning errors
35    #[error("Cache error: {0}")]
36    Cache(#[from] CacheError),
37
38    /// Semantic analysis errors
39    #[error("Semantic error: {0}")]
40    Semantic(#[from] SemanticError),
41
42    /// I/O errors
43    #[error("I/O error: {0}")]
44    Io(#[from] std::io::Error),
45
46    /// Security scan found issues
47    #[error("Security scan found {count} issues ({critical} critical)")]
48    SecurityIssues {
49        /// Total number of issues
50        count: usize,
51        /// Number of critical issues
52        critical: usize,
53    },
54
55    /// Token budget exceeded
56    #[error("Token budget exceeded: {used} tokens used, {budget} allowed")]
57    BudgetExceeded {
58        /// Tokens used
59        used: u32,
60        /// Budget limit
61        budget: u32,
62    },
63
64    /// Invalid input
65    #[error("Invalid input: {0}")]
66    InvalidInput(String),
67
68    /// Operation not supported
69    #[error("Operation not supported: {0}")]
70    NotSupported(String),
71}
72
73/// Convenience type alias for Results using InfiniloomError
74pub type Result<T> = std::result::Result<T, InfiniloomError>;
75
76impl InfiniloomError {
77    /// Create a security issues error
78    pub fn security_issues(count: usize, critical: usize) -> Self {
79        Self::SecurityIssues { count, critical }
80    }
81
82    /// Create a budget exceeded error
83    pub fn budget_exceeded(used: u32, budget: u32) -> Self {
84        Self::BudgetExceeded { used, budget }
85    }
86
87    /// Create an invalid input error
88    pub fn invalid_input(msg: impl Into<String>) -> Self {
89        Self::InvalidInput(msg.into())
90    }
91
92    /// Create a not supported error
93    pub fn not_supported(msg: impl Into<String>) -> Self {
94        Self::NotSupported(msg.into())
95    }
96
97    /// Check if this is a recoverable error
98    pub fn is_recoverable(&self) -> bool {
99        matches!(
100            self,
101            Self::SecurityIssues { .. } | Self::BudgetExceeded { .. } | Self::InvalidInput(_)
102        )
103    }
104
105    /// Check if this is a critical error
106    pub fn is_critical(&self) -> bool {
107        matches!(self, Self::Parser(_) | Self::Git(_) | Self::Io(_))
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_error_display() {
117        let err = InfiniloomError::security_issues(5, 2);
118        assert_eq!(err.to_string(), "Security scan found 5 issues (2 critical)");
119
120        let err = InfiniloomError::budget_exceeded(150000, 100000);
121        assert_eq!(err.to_string(), "Token budget exceeded: 150000 tokens used, 100000 allowed");
122    }
123
124    #[test]
125    fn test_error_classification() {
126        let err = InfiniloomError::security_issues(1, 0);
127        assert!(err.is_recoverable());
128        assert!(!err.is_critical());
129
130        let err = InfiniloomError::Io(std::io::Error::new(std::io::ErrorKind::NotFound, "test"));
131        assert!(!err.is_recoverable());
132        assert!(err.is_critical());
133    }
134
135    #[test]
136    fn test_from_io_error() {
137        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
138        let err: InfiniloomError = io_err.into();
139        assert!(matches!(err, InfiniloomError::Io(_)));
140    }
141}