vika_cli/config/
model.rs

1use serde::{Deserialize, Serialize};
2
3/// Main configuration structure for vika-cli.
4///
5/// Represents the `.vika.json` configuration file that controls
6/// code generation behavior, output directories, and module selection.
7///
8/// # Example
9///
10/// ```no_run
11/// use vika_cli::Config;
12///
13/// let config = Config::default();
14/// println!("Root directory: {}", config.root_dir);
15/// ```
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Config {
18    #[serde(rename = "$schema", default = "default_schema")]
19    pub schema: String,
20
21    #[serde(default = "default_root_dir")]
22    pub root_dir: String,
23
24    #[serde(default)]
25    pub schemas: SchemasConfig,
26
27    #[serde(default)]
28    pub apis: ApisConfig,
29
30    #[serde(default)]
31    pub modules: ModulesConfig,
32
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub spec_path: Option<String>,
35}
36
37pub fn default_schema() -> String {
38    "https://raw.githubusercontent.com/vikarno/vika-cli/main/schema/vika-config.schema.json"
39        .to_string()
40}
41
42fn default_root_dir() -> String {
43    "src".to_string()
44}
45
46/// Configuration for schema generation (TypeScript types and Zod schemas).
47///
48/// Controls where schemas are generated and how they are named.
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct SchemasConfig {
51    #[serde(default = "default_schemas_output")]
52    pub output: String,
53
54    #[serde(default = "default_naming")]
55    pub naming: String,
56}
57
58fn default_naming() -> String {
59    "PascalCase".to_string()
60}
61
62fn default_schemas_output() -> String {
63    "src/schemas".to_string()
64}
65
66/// Configuration for API client generation.
67///
68/// Controls API client output location, style, base URL, and header strategy.
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct ApisConfig {
71    #[serde(default = "default_apis_output")]
72    pub output: String,
73
74    #[serde(default = "default_style")]
75    pub style: String,
76
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub base_url: Option<String>,
79
80    #[serde(default = "default_header_strategy")]
81    pub header_strategy: String,
82}
83
84fn default_header_strategy() -> String {
85    "consumerInjected".to_string()
86}
87
88fn default_apis_output() -> String {
89    "src/apis".to_string()
90}
91
92fn default_style() -> String {
93    "fetch".to_string()
94}
95
96/// Configuration for module selection and filtering.
97///
98/// Controls which OpenAPI tags/modules are included or excluded from generation.
99#[derive(Debug, Clone, Serialize, Deserialize, Default)]
100pub struct ModulesConfig {
101    #[serde(default)]
102    pub ignore: Vec<String>,
103
104    #[serde(default)]
105    pub selected: Vec<String>,
106}
107
108impl Default for Config {
109    fn default() -> Self {
110        Self {
111            schema: default_schema(),
112            root_dir: default_root_dir(),
113            schemas: SchemasConfig {
114                output: default_schemas_output(),
115                naming: default_naming(),
116            },
117            apis: ApisConfig {
118                output: default_apis_output(),
119                style: default_style(),
120                base_url: None,
121                header_strategy: default_header_strategy(),
122            },
123            modules: ModulesConfig {
124                ignore: vec![],
125                selected: vec![],
126            },
127            spec_path: None,
128        }
129    }
130}
131
132impl Default for SchemasConfig {
133    fn default() -> Self {
134        Self {
135            output: default_schemas_output(),
136            naming: default_naming(),
137        }
138    }
139}
140
141impl Default for ApisConfig {
142    fn default() -> Self {
143        Self {
144            output: default_apis_output(),
145            style: default_style(),
146            base_url: None,
147            header_strategy: default_header_strategy(),
148        }
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn test_load_default_config() {
158        let config = Config::default();
159        assert_eq!(config.root_dir, "src");
160        assert_eq!(config.schemas.output, "src/schemas");
161        assert_eq!(config.apis.output, "src/apis");
162        assert_eq!(config.apis.style, "fetch");
163        assert!(!config.schema.is_empty());
164    }
165
166    #[test]
167    fn test_config_serialization() {
168        let config = Config::default();
169        let json = serde_json::to_string_pretty(&config).unwrap();
170
171        assert!(json.contains("\"root_dir\""));
172        assert!(json.contains("\"schemas\""));
173        assert!(json.contains("\"apis\""));
174        assert!(json.contains("\"$schema\""));
175    }
176
177    #[test]
178    fn test_config_deserialization() {
179        let json = r#"
180        {
181            "$schema": "https://example.com/schema.json",
182            "root_dir": "test",
183            "schemas": {
184                "output": "test/schemas",
185                "naming": "camelCase"
186            },
187            "apis": {
188                "output": "test/apis",
189                "style": "fetch",
190                "header_strategy": "bearerToken"
191            },
192            "modules": {
193                "ignore": ["test"],
194                "selected": []
195            }
196        }
197        "#;
198
199        let config: Config = serde_json::from_str(json).unwrap();
200        assert_eq!(config.root_dir, "test");
201        assert_eq!(config.schemas.output, "test/schemas");
202        assert_eq!(config.schemas.naming, "camelCase");
203        assert_eq!(config.apis.header_strategy, "bearerToken");
204        assert_eq!(config.modules.ignore, vec!["test"]);
205    }
206
207    #[test]
208    fn test_config_with_base_url() {
209        let mut config = Config::default();
210        config.apis.base_url = Some("/api/v1".to_string());
211
212        let json = serde_json::to_string_pretty(&config).unwrap();
213        assert!(json.contains("\"base_url\""));
214        assert!(json.contains("/api/v1"));
215    }
216
217    #[test]
218    fn test_config_schema_field() {
219        let config = Config::default();
220        let json = serde_json::to_string_pretty(&config).unwrap();
221
222        // Check that $schema is included
223        assert!(json.contains("\"$schema\""));
224    }
225}