1use thiserror::Error;
2
3#[derive(Debug, Error)]
27pub enum VikaError {
28 #[error("Schema error: {0}")]
29 Schema(#[from] SchemaError),
30
31 #[error("Config error: {0}")]
32 Config(#[from] ConfigError),
33
34 #[error("Network error: {0}")]
35 Network(#[from] NetworkError),
36
37 #[error("File system error: {0}")]
38 FileSystem(#[from] FileSystemError),
39
40 #[error("Generation error: {0}")]
41 Generation(#[from] GenerationError),
42
43 #[error("Validation error: {0}")]
44 Validation(#[from] ValidationError),
45}
46
47#[derive(Debug, Error)]
48pub enum SchemaError {
49 #[error("Schema not found: {name}")]
50 NotFound { name: String },
51
52 #[error("Invalid schema reference: {ref_path}")]
53 InvalidReference { ref_path: String },
54
55 #[error("Circular reference detected: {cycle:?}")]
56 CircularReference { cycle: Vec<String> },
57
58 #[error("Unsupported schema type: {schema_type}")]
59 UnsupportedType { schema_type: String },
60
61 #[error("Parameter not found: {name}")]
62 ParameterNotFound { name: String },
63
64 #[error("Request body not found: {name}")]
65 RequestBodyNotFound { name: String },
66
67 #[error("Response not found: {name}")]
68 ResponseNotFound { name: String },
69
70 #[error("Unsupported reference path: {ref_path}")]
71 UnsupportedReferencePath { ref_path: String },
72}
73
74#[derive(Debug, Error)]
75pub enum ConfigError {
76 #[error("Config file not found: {path}")]
77 NotFound { path: String },
78
79 #[error("Invalid config: {message}")]
80 Invalid { message: String },
81
82 #[error("Failed to read config: {0}")]
83 ReadError(#[from] std::io::Error),
84
85 #[error("Failed to parse config: {0}")]
86 ParseError(#[from] serde_json::Error),
87
88 #[error("Required field missing: {field}")]
89 MissingField { field: String },
90
91 #[error("Invalid output directory: {path}")]
92 InvalidOutputDirectory { path: String },
93}
94
95#[derive(Debug, Error)]
96pub enum NetworkError {
97 #[error("Failed to fetch spec from {url}: {source}")]
98 FetchFailed {
99 url: String,
100 #[source]
101 source: reqwest::Error,
102 },
103
104 #[error("Failed to read response from {url}: {source}")]
105 ReadResponseFailed {
106 url: String,
107 #[source]
108 source: reqwest::Error,
109 },
110
111 #[error("Invalid URL: {url}")]
112 InvalidUrl { url: String },
113}
114
115#[derive(Debug, Error)]
116pub enum FileSystemError {
117 #[error("Failed to create directory: {path}")]
118 CreateDirectoryFailed {
119 path: String,
120 #[source]
121 source: std::io::Error,
122 },
123
124 #[error("Failed to read file: {path}")]
125 ReadFileFailed {
126 path: String,
127 #[source]
128 source: std::io::Error,
129 },
130
131 #[error("Failed to write file: {path}")]
132 WriteFileFailed {
133 path: String,
134 #[source]
135 source: std::io::Error,
136 },
137
138 #[error("File not found: {path}")]
139 FileNotFound { path: String },
140
141 #[error("Directory not found: {path}")]
142 DirectoryNotFound { path: String },
143
144 #[error("File was modified by user: {path}\n This file was previously generated by vika-cli but has been modified.\n Use --force to overwrite, or --backup to create a backup first.")]
145 FileModifiedByUser { path: String },
146}
147
148#[derive(Debug, Error)]
149pub enum GenerationError {
150 #[error("No modules available after filtering")]
151 NoModulesAvailable,
152
153 #[error("No modules selected")]
154 NoModulesSelected,
155
156 #[error("Spec path or URL is required. Use --spec <path-or-url>")]
157 SpecPathRequired,
158
159 #[error("Failed to generate TypeScript types: {0}")]
160 TypeScriptGenerationFailed(String),
161
162 #[error("Failed to generate Zod schemas: {0}")]
163 ZodGenerationFailed(String),
164
165 #[error("Failed to generate API client: {0}")]
166 ApiClientGenerationFailed(String),
167
168 #[error("Invalid operation: {message}")]
169 InvalidOperation { message: String },
170}
171
172#[derive(Debug, Error)]
173pub enum ValidationError {
174 #[error("Invalid TypeScript identifier: {name}")]
175 InvalidTypeScriptIdentifier { name: String },
176
177 #[error("Invalid module name: {name}")]
178 InvalidModuleName { name: String },
179
180 #[error("Validation failed: {message}")]
181 Failed { message: String },
182}
183
184pub type Result<T> = std::result::Result<T, VikaError>;
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_schema_error_display() {
192 let error = SchemaError::NotFound {
193 name: "User".to_string(),
194 };
195 let error_msg = error.to_string();
196 assert!(error_msg.contains("User"));
197 assert!(error_msg.contains("not found"));
198 }
199
200 #[test]
201 fn test_config_error_display() {
202 let error = ConfigError::Invalid {
203 message: "Test error".to_string(),
204 };
205 let error_msg = error.to_string();
206 assert!(error_msg.contains("Test error"));
207 }
208
209 #[test]
210 fn test_network_error_display() {
211 let error = NetworkError::InvalidUrl {
212 url: "invalid-url".to_string(),
213 };
214 let error_msg = error.to_string();
215 assert!(error_msg.contains("invalid-url"));
216 }
217
218 #[test]
219 fn test_file_system_error_display() {
220 let error = FileSystemError::FileNotFound {
221 path: "/test/path".to_string(),
222 };
223 let error_msg = error.to_string();
224 assert!(error_msg.contains("/test/path"));
225 }
226
227 #[test]
228 fn test_generation_error_display() {
229 let error = GenerationError::NoModulesAvailable;
230 let error_msg = error.to_string();
231 assert!(error_msg.contains("No modules available"));
232 }
233
234 #[test]
235 fn test_error_conversion() {
236 let schema_error = SchemaError::NotFound {
237 name: "Test".to_string(),
238 };
239 let vika_error: VikaError = schema_error.into();
240 let error_msg = vika_error.to_string();
241 assert!(error_msg.contains("Schema error"));
242 }
243
244 #[test]
245 fn test_error_context_preservation() {
246 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found");
247 let config_error = ConfigError::ReadError(io_error);
248 let vika_error: VikaError = config_error.into();
249 let error_msg = vika_error.to_string();
250 assert!(error_msg.contains("Config error"));
251 }
252}