use std::fmt;
use std::path::PathBuf;
#[derive(Debug)]
pub enum ShellError {
ModelNotFound { path: PathBuf, hint: String },
ModelCorrupted { path: PathBuf, hint: String },
ModelLoadFailed { path: PathBuf, cause: String },
InvalidInput { message: String },
HistoryParseError {
path: PathBuf,
line: usize,
cause: String,
},
SecurityViolation { command: String, reason: String },
}
impl fmt::Display for ShellError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ModelNotFound { path, hint } => {
write!(
f,
"Error: Model not found at '{}'\nHint: {}",
path.display(),
hint
)
}
Self::ModelCorrupted { path, hint } => {
write!(
f,
"Error: Model corrupted at '{}'\nHint: {}",
path.display(),
hint
)
}
Self::ModelLoadFailed { path, cause } => {
write!(
f,
"Error: Failed to load model '{}': {}",
path.display(),
cause
)
}
Self::InvalidInput { message } => {
write!(f, "Error: {message}")
}
Self::HistoryParseError { path, line, cause } => {
write!(
f,
"Error: Failed to parse {} at line {}: {}",
path.display(),
line,
cause
)
}
Self::SecurityViolation { command, reason } => {
write!(f, "Security: Blocked '{}' - {}", command, reason)
}
}
}
}
impl std::error::Error for ShellError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_model_not_found_display() {
let err = ShellError::ModelNotFound {
path: PathBuf::from("/path/to/model.bin"),
hint: "Run 'aprender-shell train' to create a model".into(),
};
let msg = err.to_string();
assert!(msg.contains("not found"));
assert!(msg.contains("/path/to/model.bin"));
assert!(msg.contains("Hint:"));
}
#[test]
fn test_model_corrupted_display() {
let err = ShellError::ModelCorrupted {
path: PathBuf::from("/path/to/model.bin"),
hint: "Run 'aprender-shell train' to rebuild".into(),
};
let msg = err.to_string();
assert!(msg.contains("corrupted"));
assert!(msg.contains("rebuild"));
}
#[test]
fn test_invalid_input_display() {
let err = ShellError::InvalidInput {
message: "Empty prefix".into(),
};
assert_eq!(err.to_string(), "Error: Empty prefix");
}
#[test]
fn test_security_violation_display() {
let err = ShellError::SecurityViolation {
command: "export SECRET=abc".into(),
reason: "Contains sensitive data".into(),
};
let msg = err.to_string();
assert!(msg.contains("Security:"));
assert!(msg.contains("Blocked"));
}
}