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 #[error("No specs are defined. At least one spec must be configured in the 'specs' array.")]
95 NoSpecDefined,
96
97 #[error("Duplicate spec name found: {name}")]
98 DuplicateSpecName { name: String },
99
100 #[error("Invalid spec name: {name}. Spec names must be non-empty and contain only alphanumeric characters, hyphens, and underscores.")]
101 InvalidSpecName { name: String },
102}
103
104#[derive(Debug, Error)]
105pub enum NetworkError {
106 #[error("Failed to fetch spec from {url}: {source}")]
107 FetchFailed {
108 url: String,
109 #[source]
110 source: reqwest::Error,
111 },
112
113 #[error("Failed to read response from {url}: {source}")]
114 ReadResponseFailed {
115 url: String,
116 #[source]
117 source: reqwest::Error,
118 },
119
120 #[error("Invalid URL: {url}")]
121 InvalidUrl { url: String },
122}
123
124#[derive(Debug, Error)]
125pub enum FileSystemError {
126 #[error("Failed to create directory: {path}")]
127 CreateDirectoryFailed {
128 path: String,
129 #[source]
130 source: std::io::Error,
131 },
132
133 #[error("Failed to read file: {path}")]
134 ReadFileFailed {
135 path: String,
136 #[source]
137 source: std::io::Error,
138 },
139
140 #[error("Failed to write file: {path}")]
141 WriteFileFailed {
142 path: String,
143 #[source]
144 source: std::io::Error,
145 },
146
147 #[error("File not found: {path}")]
148 FileNotFound { path: String },
149
150 #[error("Directory not found: {path}")]
151 DirectoryNotFound { path: String },
152
153 #[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.")]
154 FileModifiedByUser { path: String },
155}
156
157#[derive(Debug, Error)]
158pub enum GenerationError {
159 #[error("No modules available after filtering")]
160 NoModulesAvailable,
161
162 #[error("No modules selected")]
163 NoModulesSelected,
164
165 #[error("Spec path or URL is required. Use --spec <path-or-url>")]
166 SpecPathRequired,
167
168 #[error("Spec not found: {name}. Available specs: {available:?}")]
169 SpecNotFound {
170 name: String,
171 available: Vec<String>,
172 },
173
174 #[error("Failed to generate TypeScript types: {0}")]
175 TypeScriptGenerationFailed(String),
176
177 #[error("Failed to generate Zod schemas: {0}")]
178 ZodGenerationFailed(String),
179
180 #[error("Failed to generate API client: {0}")]
181 ApiClientGenerationFailed(String),
182
183 #[error("Invalid operation: {message}")]
184 InvalidOperation { message: String },
185
186 #[error("Template error: {0}")]
187 Template(#[from] TemplateError),
188}
189
190#[derive(Debug, Error)]
191pub enum ValidationError {
192 #[error("Invalid TypeScript identifier: {name}")]
193 InvalidTypeScriptIdentifier { name: String },
194
195 #[error("Invalid module name: {name}")]
196 InvalidModuleName { name: String },
197
198 #[error("Validation failed: {message}")]
199 Failed { message: String },
200}
201
202#[derive(Debug, Error)]
203pub enum TemplateError {
204 #[error("Template not found: {name}")]
205 NotFound { name: String },
206
207 #[error("Failed to render template {name}: {message}")]
208 RenderFailed { name: String, message: String },
209
210 #[error("Invalid template syntax in {name}: {message}")]
211 InvalidSyntax { name: String, message: String },
212
213 #[error("Missing required context field {field} in template {name}")]
214 MissingContextField { name: String, field: String },
215
216 #[error("Failed to load template {name}: {source}")]
217 LoadFailed {
218 name: String,
219 #[source]
220 source: FileSystemError,
221 },
222}
223
224pub type Result<T> = std::result::Result<T, VikaError>;
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 #[test]
231 fn test_schema_error_display() {
232 let error = SchemaError::NotFound {
233 name: "User".to_string(),
234 };
235 let error_msg = error.to_string();
236 assert!(error_msg.contains("User"));
237 assert!(error_msg.contains("not found"));
238 }
239
240 #[test]
241 fn test_config_error_display() {
242 let error = ConfigError::Invalid {
243 message: "Test error".to_string(),
244 };
245 let error_msg = error.to_string();
246 assert!(error_msg.contains("Test error"));
247 }
248
249 #[test]
250 fn test_network_error_display() {
251 let error = NetworkError::InvalidUrl {
252 url: "invalid-url".to_string(),
253 };
254 let error_msg = error.to_string();
255 assert!(error_msg.contains("invalid-url"));
256 }
257
258 #[test]
259 fn test_file_system_error_display() {
260 let error = FileSystemError::FileNotFound {
261 path: "/test/path".to_string(),
262 };
263 let error_msg = error.to_string();
264 assert!(error_msg.contains("/test/path"));
265 }
266
267 #[test]
268 fn test_generation_error_display() {
269 let error = GenerationError::NoModulesAvailable;
270 let error_msg = error.to_string();
271 assert!(error_msg.contains("No modules available"));
272 }
273
274 #[test]
275 fn test_error_conversion() {
276 let schema_error = SchemaError::NotFound {
277 name: "Test".to_string(),
278 };
279 let vika_error: VikaError = schema_error.into();
280 let error_msg = vika_error.to_string();
281 assert!(error_msg.contains("Schema error"));
282 }
283
284 #[test]
285 fn test_error_context_preservation() {
286 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found");
287 let config_error = ConfigError::ReadError(io_error);
288 let vika_error: VikaError = config_error.into();
289 let error_msg = vika_error.to_string();
290 assert!(error_msg.contains("Config error"));
291 }
292}