1use thiserror::Error;
7
8pub type NlResult<T> = Result<T, NlError>;
10
11#[derive(Error, Debug)]
13pub enum NlError {
14 #[error("Preprocessing failed: {0}")]
16 Preprocess(#[from] PreprocessError),
17
18 #[error("Entity extraction failed: {0}")]
20 Extractor(#[from] ExtractorError),
21
22 #[error("Classification failed: {0}")]
24 Classifier(#[from] ClassifierError),
25
26 #[error("Assembly failed: {0}")]
28 Assembler(#[from] AssemblerError),
29
30 #[error("Validation failed: {0}")]
32 Validator(#[from] ValidatorError),
33
34 #[error("Cache error: {0}")]
36 Cache(#[from] CacheError),
37
38 #[error("Configuration error: {0}")]
40 Config(String),
41
42 #[error("I/O error: {0}")]
44 Io(#[from] std::io::Error),
45}
46
47#[derive(Error, Debug, Clone, PartialEq, Eq)]
49pub enum PreprocessError {
50 #[error("Input too long: {len} bytes (max: {max})")]
52 InputTooLong { len: usize, max: usize },
53
54 #[error("Input is empty or contains only whitespace")]
56 EmptyInput,
57
58 #[error("Suspicious character detected: possible homoglyph attack")]
60 HomoglyphDetected,
61
62 #[error("Invalid UTF-8 encoding")]
64 InvalidUtf8,
65}
66
67#[derive(Error, Debug, Clone, PartialEq, Eq)]
69pub enum ExtractorError {
70 #[error("No symbol or pattern found in query")]
72 NoSymbolFound,
73
74 #[error("Ambiguous symbol reference: multiple interpretations possible")]
76 AmbiguousSymbol,
77
78 #[error("Unknown language: {0}")]
80 UnknownLanguage(String),
81
82 #[error("Unknown symbol kind: {0}")]
84 UnknownKind(String),
85
86 #[error("Pattern compilation failed: {0}")]
88 RegexError(String),
89}
90
91#[derive(Error, Debug)]
93pub enum ClassifierError {
94 #[error("Model not found at: {0}")]
96 ModelNotFound(String),
97
98 #[error("Model checksum mismatch: expected {expected}, got {actual}")]
100 ChecksumMismatch { expected: String, actual: String },
101
102 #[error("Tokenization failed: {0}")]
104 TokenizationFailed(String),
105
106 #[error("ONNX Runtime error: {0}")]
108 OnnxError(String),
109
110 #[error("Model version {model_version} incompatible with sqry-nl {crate_version}")]
112 VersionMismatch {
113 model_version: String,
114 crate_version: String,
115 },
116
117 #[error("Classification timed out after {timeout_ms}ms")]
119 Timeout { timeout_ms: u64 },
120}
121
122#[derive(Error, Debug, Clone, PartialEq, Eq)]
124pub enum AssemblerError {
125 #[error("Missing required symbol for this command type")]
127 MissingSymbol,
128
129 #[error("Trace-path requires both 'from' and 'to' symbols")]
131 MissingTracePath,
132
133 #[error("Cannot assemble command: intent is ambiguous")]
135 AmbiguousIntent,
136
137 #[error("Generated command too long: {len} chars (max: {max})")]
139 CommandTooLong { len: usize, max: usize },
140
141 #[error("No template found for intent: {0}")]
143 NoTemplate(String),
144}
145
146#[derive(Error, Debug, Clone, PartialEq, Eq)]
148pub enum ValidatorError {
149 #[error("Command rejected: doesn't match any allowed template")]
151 TemplateMismatch,
152
153 #[error("Command rejected: contains shell metacharacters")]
155 MetacharDetected,
156
157 #[error("Command rejected: contains environment variable")]
159 EnvVarDetected,
160
161 #[error("Command rejected: path traversal detected")]
163 PathTraversal,
164
165 #[error("Command rejected: absolute paths not allowed")]
167 AbsolutePath,
168
169 #[error("Command rejected: write operations not allowed via NL")]
171 WriteOperation,
172
173 #[error("Command rejected: exceeds maximum length")]
175 CommandTooLong,
176}
177
178#[derive(Error, Debug, Clone, PartialEq, Eq)]
180pub enum CacheError {
181 #[error("Cache is disabled")]
183 Disabled,
184
185 #[error("Cache entry has expired")]
187 Expired,
188
189 #[error("Failed to generate cache key: {0}")]
191 KeyGenerationFailed(String),
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
199 fn test_error_display() {
200 let err = PreprocessError::InputTooLong {
201 len: 5000,
202 max: 4096,
203 };
204 assert!(err.to_string().contains("5000"));
205 assert!(err.to_string().contains("4096"));
206 }
207
208 #[test]
209 fn test_error_conversion() {
210 let preprocess_err = PreprocessError::EmptyInput;
211 let nl_err: NlError = preprocess_err.into();
212 assert!(matches!(nl_err, NlError::Preprocess(_)));
213 }
214
215 #[test]
216 fn test_errors_implement_std_error() {
217 fn assert_error<T: std::error::Error>() {}
218
219 assert_error::<NlError>();
220 assert_error::<PreprocessError>();
221 assert_error::<ExtractorError>();
222 assert_error::<ClassifierError>();
223 assert_error::<AssemblerError>();
224 assert_error::<ValidatorError>();
225 assert_error::<CacheError>();
226 }
227}