tldr_cli/commands/remaining/
error.rs1use std::path::PathBuf;
8use thiserror::Error;
9
10#[derive(Debug, Error)]
12pub enum RemainingError {
13 #[error("file not found: {}", path.display())]
15 FileNotFound { path: PathBuf },
16
17 #[error("symbol '{}' not found in {}", symbol, file.display())]
19 SymbolNotFound { symbol: String, file: PathBuf },
20
21 #[error("parse error in {}: {message}", file.display())]
23 ParseError { file: PathBuf, message: String },
24
25 #[error("invalid argument: {message}")]
27 InvalidArgument { message: String },
28
29 #[error("file too large: {} ({bytes} bytes)", path.display())]
31 FileTooLarge { path: PathBuf, bytes: u64 },
32
33 #[error("path traversal blocked: {}", path.display())]
35 PathTraversal { path: PathBuf },
36
37 #[error("unsupported language: {language}")]
39 UnsupportedLanguage { language: String },
40
41 #[error("analysis error: {message}")]
43 AnalysisError { message: String },
44
45 #[error("{count} findings detected")]
47 FindingsDetected { count: u32 },
48
49 #[error("{message}")]
59 AutodetectUnsupported { message: String },
60
61 #[error("analysis timed out after {seconds}s")]
63 Timeout { seconds: u64 },
64
65 #[error("IO error: {0}")]
67 Io(#[from] std::io::Error),
68
69 #[error("JSON error: {0}")]
71 Json(#[from] serde_json::Error),
72}
73
74impl RemainingError {
75 pub fn file_not_found(path: impl Into<PathBuf>) -> Self {
77 Self::FileNotFound { path: path.into() }
78 }
79
80 pub fn symbol_not_found(symbol: impl Into<String>, file: impl Into<PathBuf>) -> Self {
82 Self::SymbolNotFound {
83 symbol: symbol.into(),
84 file: file.into(),
85 }
86 }
87
88 pub fn parse_error(file: impl Into<PathBuf>, message: impl Into<String>) -> Self {
90 Self::ParseError {
91 file: file.into(),
92 message: message.into(),
93 }
94 }
95
96 pub fn invalid_argument(message: impl Into<String>) -> Self {
98 Self::InvalidArgument {
99 message: message.into(),
100 }
101 }
102
103 pub fn file_too_large(path: impl Into<PathBuf>, bytes: u64) -> Self {
105 Self::FileTooLarge {
106 path: path.into(),
107 bytes,
108 }
109 }
110
111 pub fn path_traversal(path: impl Into<PathBuf>) -> Self {
113 Self::PathTraversal { path: path.into() }
114 }
115
116 pub fn unsupported_language(language: impl Into<String>) -> Self {
118 Self::UnsupportedLanguage {
119 language: language.into(),
120 }
121 }
122
123 pub fn analysis_error(message: impl Into<String>) -> Self {
125 Self::AnalysisError {
126 message: message.into(),
127 }
128 }
129
130 pub fn findings_detected(count: u32) -> Self {
132 Self::FindingsDetected { count }
133 }
134
135 pub fn autodetect_unsupported(message: impl Into<String>) -> Self {
139 Self::AutodetectUnsupported {
140 message: message.into(),
141 }
142 }
143
144 pub fn timeout(seconds: u64) -> Self {
146 Self::Timeout { seconds }
147 }
148
149 pub fn exit_code(&self) -> i32 {
151 match self {
152 Self::FindingsDetected { .. } => 2,
154 Self::AutodetectUnsupported { .. } => 2,
160 _ => 1, }
162 }
163}
164
165pub type RemainingResult<T> = Result<T, RemainingError>;
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_file_not_found_error() {
174 let err = RemainingError::file_not_found("/path/to/file.py");
175 assert!(err.to_string().contains("file not found"));
176 assert!(err.to_string().contains("file.py"));
177 }
178
179 #[test]
180 fn test_symbol_not_found_error() {
181 let err = RemainingError::symbol_not_found("my_function", "/path/to/file.py");
182 assert!(err.to_string().contains("my_function"));
183 assert!(err.to_string().contains("not found"));
184 }
185
186 #[test]
187 fn test_exit_codes() {
188 assert_eq!(RemainingError::file_not_found("/foo").exit_code(), 1);
189 assert_eq!(RemainingError::findings_detected(5).exit_code(), 2);
190 assert_eq!(
191 RemainingError::autodetect_unsupported("nope").exit_code(),
192 2
193 );
194 }
195}