use thiserror::Error;
use crate::config::ConfigError;
use crate::git::GitError;
use crate::incremental::CacheError;
use crate::parser::ParserError;
use crate::remote::RemoteError;
use crate::semantic::SemanticError;
#[derive(Debug, Error)]
pub enum InfiniloomError {
#[error("Parse error: {0}")]
Parser(#[from] ParserError),
#[error("Git error: {0}")]
Git(#[from] GitError),
#[error("Remote error: {0}")]
Remote(#[from] RemoteError),
#[error("Config error: {0}")]
Config(#[from] ConfigError),
#[error("Cache error: {0}")]
Cache(#[from] CacheError),
#[error("Semantic error: {0}")]
Semantic(#[from] SemanticError),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Security scan found {count} issues ({critical} critical)")]
SecurityIssues {
count: usize,
critical: usize,
},
#[error("Token budget exceeded: {used} tokens used, {budget} allowed")]
BudgetExceeded {
used: u32,
budget: u32,
},
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Operation not supported: {0}")]
NotSupported(String),
}
pub type Result<T> = std::result::Result<T, InfiniloomError>;
impl InfiniloomError {
pub fn security_issues(count: usize, critical: usize) -> Self {
Self::SecurityIssues { count, critical }
}
pub fn budget_exceeded(used: u32, budget: u32) -> Self {
Self::BudgetExceeded { used, budget }
}
pub fn invalid_input(msg: impl Into<String>) -> Self {
Self::InvalidInput(msg.into())
}
pub fn not_supported(msg: impl Into<String>) -> Self {
Self::NotSupported(msg.into())
}
pub fn is_recoverable(&self) -> bool {
matches!(
self,
Self::SecurityIssues { .. } | Self::BudgetExceeded { .. } | Self::InvalidInput(_)
)
}
pub fn is_critical(&self) -> bool {
matches!(self, Self::Parser(_) | Self::Git(_) | Self::Io(_))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = InfiniloomError::security_issues(5, 2);
assert_eq!(err.to_string(), "Security scan found 5 issues (2 critical)");
let err = InfiniloomError::budget_exceeded(150000, 100000);
assert_eq!(err.to_string(), "Token budget exceeded: 150000 tokens used, 100000 allowed");
}
#[test]
fn test_error_classification() {
let err = InfiniloomError::security_issues(1, 0);
assert!(err.is_recoverable());
assert!(!err.is_critical());
let err = InfiniloomError::Io(std::io::Error::new(std::io::ErrorKind::NotFound, "test"));
assert!(!err.is_recoverable());
assert!(err.is_critical());
}
#[test]
fn test_from_io_error() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let err: InfiniloomError = io_err.into();
assert!(matches!(err, InfiniloomError::Io(_)));
}
}