1use std::fmt;
7use std::path::PathBuf;
8
9#[derive(Debug)]
14pub enum ShellError {
15 ModelNotFound { path: PathBuf, hint: String },
17
18 ModelCorrupted { path: PathBuf, hint: String },
20
21 ModelLoadFailed { path: PathBuf, cause: String },
23
24 InvalidInput { message: String },
26
27 HistoryParseError {
29 path: PathBuf,
30 line: usize,
31 cause: String,
32 },
33
34 SecurityViolation { command: String, reason: String },
36}
37
38impl fmt::Display for ShellError {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 match self {
41 Self::ModelNotFound { path, hint } => {
42 write!(
43 f,
44 "Error: Model not found at '{}'\nHint: {}",
45 path.display(),
46 hint
47 )
48 }
49 Self::ModelCorrupted { path, hint } => {
50 write!(
51 f,
52 "Error: Model corrupted at '{}'\nHint: {}",
53 path.display(),
54 hint
55 )
56 }
57 Self::ModelLoadFailed { path, cause } => {
58 write!(
59 f,
60 "Error: Failed to load model '{}': {}",
61 path.display(),
62 cause
63 )
64 }
65 Self::InvalidInput { message } => {
66 write!(f, "Error: {message}")
67 }
68 Self::HistoryParseError { path, line, cause } => {
69 write!(
70 f,
71 "Error: Failed to parse {} at line {}: {}",
72 path.display(),
73 line,
74 cause
75 )
76 }
77 Self::SecurityViolation { command, reason } => {
78 write!(f, "Security: Blocked '{}' - {}", command, reason)
79 }
80 }
81 }
82}
83
84impl std::error::Error for ShellError {}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_model_not_found_display() {
92 let err = ShellError::ModelNotFound {
93 path: PathBuf::from("/path/to/model.bin"),
94 hint: "Run 'aprender-shell train' to create a model".into(),
95 };
96 let msg = err.to_string();
97 assert!(msg.contains("not found"));
98 assert!(msg.contains("/path/to/model.bin"));
99 assert!(msg.contains("Hint:"));
100 }
101
102 #[test]
103 fn test_model_corrupted_display() {
104 let err = ShellError::ModelCorrupted {
105 path: PathBuf::from("/path/to/model.bin"),
106 hint: "Run 'aprender-shell train' to rebuild".into(),
107 };
108 let msg = err.to_string();
109 assert!(msg.contains("corrupted"));
110 assert!(msg.contains("rebuild"));
111 }
112
113 #[test]
114 fn test_invalid_input_display() {
115 let err = ShellError::InvalidInput {
116 message: "Empty prefix".into(),
117 };
118 assert_eq!(err.to_string(), "Error: Empty prefix");
119 }
120
121 #[test]
122 fn test_security_violation_display() {
123 let err = ShellError::SecurityViolation {
124 command: "export SECRET=abc".into(),
125 reason: "Contains sensitive data".into(),
126 };
127 let msg = err.to_string();
128 assert!(msg.contains("Security:"));
129 assert!(msg.contains("Blocked"));
130 }
131}