1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct SpecEntry {
6 pub name: String,
8 pub path: String,
10
11 pub schemas: SchemasConfig,
13
14 pub apis: ApisConfig,
16
17 pub modules: ModulesConfig,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct Config {
36 #[serde(rename = "$schema", default = "default_schema")]
37 pub schema: String,
38
39 #[serde(default = "default_root_dir")]
40 pub root_dir: String,
41
42 #[serde(default)]
43 pub generation: GenerationConfig,
44
45 #[serde(default)]
47 pub specs: Vec<SpecEntry>,
48}
49
50pub fn default_schema() -> String {
51 "https://raw.githubusercontent.com/vikarno/vika-cli/main/schema/vika-config.schema.json"
52 .to_string()
53}
54
55fn default_root_dir() -> String {
56 "src".to_string()
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct SchemasConfig {
64 #[serde(default = "default_schemas_output")]
65 pub output: String,
66
67 #[serde(default = "default_naming")]
68 pub naming: String,
69}
70
71fn default_naming() -> String {
72 "PascalCase".to_string()
73}
74
75fn default_schemas_output() -> String {
76 "src/schemas".to_string()
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct ApisConfig {
84 #[serde(default = "default_apis_output")]
85 pub output: String,
86
87 #[serde(default = "default_style")]
88 pub style: String,
89
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub base_url: Option<String>,
92
93 #[serde(default = "default_header_strategy")]
94 pub header_strategy: String,
95}
96
97fn default_header_strategy() -> String {
98 "consumerInjected".to_string()
99}
100
101fn default_apis_output() -> String {
102 "src/apis".to_string()
103}
104
105fn default_style() -> String {
106 "fetch".to_string()
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize, Default)]
113pub struct ModulesConfig {
114 #[serde(default)]
115 pub ignore: Vec<String>,
116
117 #[serde(default)]
118 pub selected: Vec<String>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct GenerationConfig {
126 #[serde(default = "default_enable_cache")]
127 pub enable_cache: bool,
128
129 #[serde(default = "default_enable_backup")]
130 pub enable_backup: bool,
131
132 #[serde(default = "default_conflict_strategy")]
133 pub conflict_strategy: String,
134}
135
136fn default_enable_cache() -> bool {
137 true
138}
139
140fn default_enable_backup() -> bool {
141 false
142}
143
144fn default_conflict_strategy() -> String {
145 "ask".to_string()
146}
147
148impl Default for GenerationConfig {
149 fn default() -> Self {
150 Self {
151 enable_cache: default_enable_cache(),
152 enable_backup: default_enable_backup(),
153 conflict_strategy: default_conflict_strategy(),
154 }
155 }
156}
157
158impl Default for Config {
159 fn default() -> Self {
160 Self {
161 schema: default_schema(),
162 root_dir: default_root_dir(),
163 generation: GenerationConfig::default(),
164 specs: vec![],
165 }
166 }
167}
168
169impl Default for SchemasConfig {
170 fn default() -> Self {
171 Self {
172 output: default_schemas_output(),
173 naming: default_naming(),
174 }
175 }
176}
177
178impl Default for ApisConfig {
179 fn default() -> Self {
180 Self {
181 output: default_apis_output(),
182 style: default_style(),
183 base_url: None,
184 header_strategy: default_header_strategy(),
185 }
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn test_load_default_config() {
195 let config = Config::default();
196 assert_eq!(config.root_dir, "src");
197 assert!(!config.schema.is_empty());
198 assert_eq!(config.specs.len(), 0);
199 }
200
201 #[test]
202 fn test_config_serialization() {
203 let config = Config::default();
204 let json = serde_json::to_string_pretty(&config).unwrap();
205
206 assert!(json.contains("\"root_dir\""));
207 assert!(json.contains("\"specs\""));
208 assert!(json.contains("\"$schema\""));
209 }
210
211 #[test]
212 fn test_config_deserialization() {
213 let json = r#"
214 {
215 "$schema": "https://example.com/schema.json",
216 "root_dir": "test",
217 "specs": [
218 {
219 "name": "test-spec",
220 "path": "test.yaml",
221 "schemas": {
222 "output": "test/schemas",
223 "naming": "camelCase"
224 },
225 "apis": {
226 "output": "test/apis",
227 "style": "fetch",
228 "header_strategy": "bearerToken"
229 },
230 "modules": {
231 "ignore": ["test"],
232 "selected": []
233 }
234 }
235 ]
236 }
237 "#;
238
239 let config: Config = serde_json::from_str(json).unwrap();
240 assert_eq!(config.root_dir, "test");
241 assert_eq!(config.specs.len(), 1);
242 assert_eq!(config.specs[0].schemas.output, "test/schemas");
243 assert_eq!(config.specs[0].schemas.naming, "camelCase");
244 assert_eq!(config.specs[0].apis.header_strategy, "bearerToken");
245 assert_eq!(config.specs[0].modules.ignore, vec!["test"]);
246 }
247
248 #[test]
249 fn test_schemas_config_default() {
250 let config = SchemasConfig::default();
251 assert_eq!(config.output, "src/schemas");
252 assert_eq!(config.naming, "PascalCase");
253 }
254
255 #[test]
256 fn test_apis_config_default() {
257 let config = ApisConfig::default();
258 assert_eq!(config.output, "src/apis");
259 assert_eq!(config.style, "fetch");
260 assert_eq!(config.header_strategy, "consumerInjected");
261 assert!(config.base_url.is_none());
262 }
263
264 #[test]
265 fn test_config_with_base_url() {
266 let mut config = Config::default();
267 config.specs.push(SpecEntry {
268 name: "test".to_string(),
269 path: "test.yaml".to_string(),
270 schemas: SchemasConfig::default(),
271 apis: ApisConfig {
272 base_url: Some("/api/v1".to_string()),
273 ..ApisConfig::default()
274 },
275 modules: ModulesConfig::default(),
276 });
277
278 let json = serde_json::to_string_pretty(&config).unwrap();
279 assert!(json.contains("\"base_url\""));
280 assert!(json.contains("/api/v1"));
281 }
282
283 #[test]
284 fn test_config_schema_field() {
285 let config = Config::default();
286 let json = serde_json::to_string_pretty(&config).unwrap();
287
288 assert!(json.contains("\"$schema\""));
290 }
291
292 #[test]
293 fn test_generation_config_defaults() {
294 let config = Config::default();
295 assert!(config.generation.enable_cache);
296 assert!(!config.generation.enable_backup);
297 assert_eq!(config.generation.conflict_strategy, "ask");
298 }
299
300 #[test]
301 fn test_config_with_generation_settings() {
302 let json = r#"
303 {
304 "$schema": "https://example.com/schema.json",
305 "root_dir": "test",
306 "schemas": {
307 "output": "test/schemas",
308 "naming": "camelCase"
309 },
310 "apis": {
311 "output": "test/apis",
312 "style": "fetch",
313 "header_strategy": "bearerToken"
314 },
315 "modules": {
316 "ignore": ["test"],
317 "selected": []
318 },
319 "generation": {
320 "enable_cache": false,
321 "enable_backup": true,
322 "conflict_strategy": "force"
323 }
324 }
325 "#;
326
327 let config: Config = serde_json::from_str(json).unwrap();
328 assert!(!config.generation.enable_cache);
329 assert!(config.generation.enable_backup);
330 assert_eq!(config.generation.conflict_strategy, "force");
331 }
332
333 #[test]
334 fn test_multi_spec_deserialization() {
335 let json = r#"
336 {
337 "$schema": "https://example.com/schema.json",
338 "specs": [
339 {
340 "name": "auth",
341 "path": "specs/auth.yaml",
342 "schemas": {},
343 "apis": {},
344 "modules": {}
345 },
346 {
347 "name": "orders",
348 "path": "specs/orders.json",
349 "schemas": {},
350 "apis": {},
351 "modules": {}
352 },
353 {
354 "name": "products",
355 "path": "specs/products.yaml",
356 "schemas": {},
357 "apis": {},
358 "modules": {}
359 }
360 ]
361 }
362 "#;
363
364 let config: Config = serde_json::from_str(json).unwrap();
365 assert_eq!(config.specs.len(), 3);
366 assert_eq!(config.specs[0].name, "auth");
367 assert_eq!(config.specs[0].path, "specs/auth.yaml");
368 assert_eq!(config.specs[1].name, "orders");
369 assert_eq!(config.specs[1].path, "specs/orders.json");
370 assert_eq!(config.specs[2].name, "products");
371 assert_eq!(config.specs[2].path, "specs/products.yaml");
372 }
373
374 #[test]
375 fn test_spec_entry_serialization() {
376 let entry = SpecEntry {
377 name: "test-spec".to_string(),
378 path: "specs/test.yaml".to_string(),
379 schemas: SchemasConfig::default(),
380 apis: ApisConfig::default(),
381 modules: ModulesConfig::default(),
382 };
383 let json = serde_json::to_string(&entry).unwrap();
384 assert!(json.contains("\"name\""));
385 assert!(json.contains("\"path\""));
386 assert!(json.contains("test-spec"));
387 assert!(json.contains("specs/test.yaml"));
388 }
389}