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("analysis timed out after {seconds}s")]
51 Timeout { seconds: u64 },
52
53 #[error("IO error: {0}")]
55 Io(#[from] std::io::Error),
56
57 #[error("JSON error: {0}")]
59 Json(#[from] serde_json::Error),
60}
61
62impl RemainingError {
63 pub fn file_not_found(path: impl Into<PathBuf>) -> Self {
65 Self::FileNotFound { path: path.into() }
66 }
67
68 pub fn symbol_not_found(symbol: impl Into<String>, file: impl Into<PathBuf>) -> Self {
70 Self::SymbolNotFound {
71 symbol: symbol.into(),
72 file: file.into(),
73 }
74 }
75
76 pub fn parse_error(file: impl Into<PathBuf>, message: impl Into<String>) -> Self {
78 Self::ParseError {
79 file: file.into(),
80 message: message.into(),
81 }
82 }
83
84 pub fn invalid_argument(message: impl Into<String>) -> Self {
86 Self::InvalidArgument {
87 message: message.into(),
88 }
89 }
90
91 pub fn file_too_large(path: impl Into<PathBuf>, bytes: u64) -> Self {
93 Self::FileTooLarge {
94 path: path.into(),
95 bytes,
96 }
97 }
98
99 pub fn path_traversal(path: impl Into<PathBuf>) -> Self {
101 Self::PathTraversal { path: path.into() }
102 }
103
104 pub fn unsupported_language(language: impl Into<String>) -> Self {
106 Self::UnsupportedLanguage {
107 language: language.into(),
108 }
109 }
110
111 pub fn analysis_error(message: impl Into<String>) -> Self {
113 Self::AnalysisError {
114 message: message.into(),
115 }
116 }
117
118 pub fn findings_detected(count: u32) -> Self {
120 Self::FindingsDetected { count }
121 }
122
123 pub fn timeout(seconds: u64) -> Self {
125 Self::Timeout { seconds }
126 }
127
128 pub fn exit_code(&self) -> i32 {
130 match self {
131 Self::FindingsDetected { .. } => 2, _ => 1, }
134 }
135}
136
137pub type RemainingResult<T> = Result<T, RemainingError>;
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn test_file_not_found_error() {
146 let err = RemainingError::file_not_found("/path/to/file.py");
147 assert!(err.to_string().contains("file not found"));
148 assert!(err.to_string().contains("file.py"));
149 }
150
151 #[test]
152 fn test_symbol_not_found_error() {
153 let err = RemainingError::symbol_not_found("my_function", "/path/to/file.py");
154 assert!(err.to_string().contains("my_function"));
155 assert!(err.to_string().contains("not found"));
156 }
157
158 #[test]
159 fn test_exit_codes() {
160 assert_eq!(RemainingError::file_not_found("/foo").exit_code(), 1);
161 assert_eq!(RemainingError::findings_detected(5).exit_code(), 2);
162 }
163}