vika_cli/commands/
init.rs1use crate::config::loader::save_config;
2use crate::config::model::Config;
3use crate::config::validator::validate_config;
4use crate::error::{GenerationError, Result};
5use crate::generator::writer::{ensure_directory, write_http_client_template};
6use colored::*;
7use dialoguer::{Confirm, Input, Select};
8use std::path::PathBuf;
9
10pub fn run() -> Result<()> {
11 println!("{}", "Initializing vika-cli project...".bright_cyan());
12 println!();
13
14 let config_path = PathBuf::from(".vika.json");
16 if config_path.exists() {
17 println!(
18 "{}",
19 "⚠️ .vika.json already exists. Skipping config creation.".yellow()
20 );
21 } else {
22 println!("{}", "Let's configure your vika-cli preferences:".bright_cyan());
23 println!();
24
25 println!("{}", "📁 Paths Configuration".bright_yellow());
27 println!();
28
29 let root_dir: String = Input::new()
30 .with_prompt("Root directory for generated code")
31 .default("src".to_string())
32 .interact_text()
33 .map_err(|e| GenerationError::InvalidOperation {
34 message: format!("Failed to get user input: {}", e),
35 })?;
36
37 println!();
38
39 let schemas_output: String = Input::new()
40 .with_prompt("Schemas output directory")
41 .default("src/schemas".to_string())
42 .interact_text()
43 .map_err(|e| GenerationError::InvalidOperation {
44 message: format!("Failed to get user input: {}", e),
45 })?;
46
47 println!();
48
49 let apis_output: String = Input::new()
50 .with_prompt("APIs output directory")
51 .default("src/apis".to_string())
52 .interact_text()
53 .map_err(|e| GenerationError::InvalidOperation {
54 message: format!("Failed to get user input: {}", e),
55 })?;
56
57 println!();
58
59 let naming_options = vec!["PascalCase", "camelCase", "snake_case", "kebab-case"];
61 let naming_index = Select::new()
62 .with_prompt("Schema naming convention")
63 .items(&[
64 "PascalCase - ProductDto, UserProfile (recommended)",
65 "camelCase - productDto, userProfile",
66 "snake_case - product_dto, user_profile",
67 "kebab-case - product-dto, user-profile",
68 ])
69 .default(0)
70 .interact()
71 .map_err(|e| GenerationError::InvalidOperation {
72 message: format!("Failed to get user selection: {}", e),
73 })?;
74
75 let naming = naming_options[naming_index].to_string();
76
77 println!();
78
79 println!("{}", "🔌 API Configuration".bright_yellow());
81 println!();
82
83 let api_style_options = vec!["fetch"];
84 let api_style_index = Select::new()
85 .with_prompt("API client style")
86 .items(&["fetch - Native Fetch API (recommended)"])
87 .default(0)
88 .interact()
89 .map_err(|e| GenerationError::InvalidOperation {
90 message: format!("Failed to get user selection: {}", e),
91 })?;
92
93 let api_style = api_style_options[api_style_index].to_string();
94
95 println!();
96
97 let base_url_input: String = Input::new()
98 .with_prompt("API base URL (optional, press Enter to skip)")
99 .allow_empty(true)
100 .interact_text()
101 .map_err(|e| GenerationError::InvalidOperation {
102 message: format!("Failed to get user input: {}", e),
103 })?;
104
105 let base_url = if base_url_input.trim().is_empty() {
106 None
107 } else {
108 Some(base_url_input.trim().to_string())
109 };
110
111 println!();
112
113 let header_strategy_options = vec!["consumerInjected", "bearerToken", "fixed"];
114 let header_strategy_index = Select::new()
115 .with_prompt("Header strategy for API requests")
116 .items(&[
117 "consumerInjected - Headers provided by consumer (recommended)",
118 "bearerToken - Automatic Bearer token injection",
119 "fixed - Fixed headers from config",
120 ])
121 .default(0)
122 .interact()
123 .map_err(|e| GenerationError::InvalidOperation {
124 message: format!("Failed to get user selection: {}", e),
125 })?;
126
127 let header_strategy = header_strategy_options[header_strategy_index].to_string();
128
129 println!();
130
131 println!("{}", "⚙️ Generation Preferences".bright_yellow());
133 println!();
134
135 let enable_cache = Confirm::new()
136 .with_prompt("Enable caching for faster regeneration?")
137 .default(true)
138 .interact()
139 .map_err(|e| GenerationError::InvalidOperation {
140 message: format!("Failed to get user input: {}", e),
141 })?;
142
143 println!();
144
145 let enable_backup = Confirm::new()
146 .with_prompt("Enable automatic backups before overwriting files?")
147 .default(false)
148 .interact()
149 .map_err(|e| GenerationError::InvalidOperation {
150 message: format!("Failed to get user input: {}", e),
151 })?;
152
153 println!();
154
155 let conflict_strategy_options = vec!["ask", "force", "skip"];
156 let conflict_strategy_index = Select::new()
157 .with_prompt("What should happen when a file was modified by you?")
158 .items(&[
159 "ask - Prompt before overwriting (recommended)",
160 "force - Always overwrite without asking",
161 "skip - Skip modified files",
162 ])
163 .default(0)
164 .interact()
165 .map_err(|e| GenerationError::InvalidOperation {
166 message: format!("Failed to get user selection: {}", e),
167 })?;
168
169 let conflict_strategy = conflict_strategy_options[conflict_strategy_index].to_string();
170
171 println!();
172
173 let mut config = Config::default();
175 config.root_dir = root_dir;
176 config.schemas.output = schemas_output;
177 config.schemas.naming = naming;
178 config.apis.output = apis_output;
179 config.apis.style = api_style;
180 config.apis.base_url = base_url;
181 config.apis.header_strategy = header_strategy;
182 config.generation.enable_cache = enable_cache;
183 config.generation.enable_backup = enable_backup;
184 config.generation.conflict_strategy = conflict_strategy;
185
186 validate_config(&config)?;
187 save_config(&config)?;
188 println!("{}", "✅ Created .vika.json".green());
189 }
190
191 let config = crate::config::loader::load_config()?;
193
194 let root_dir = PathBuf::from(&config.root_dir);
195 ensure_directory(&root_dir)?;
196
197 let schemas_dir = PathBuf::from(&config.schemas.output);
198 ensure_directory(&schemas_dir)?;
199
200 let apis_dir = PathBuf::from(&config.apis.output);
201 ensure_directory(&apis_dir)?;
202
203 let http_client_path = apis_dir.join("http.ts");
205 if !http_client_path.exists() {
206 write_http_client_template(&http_client_path)?;
207 println!(
208 "{}",
209 format!("✅ Created {}", http_client_path.display()).green()
210 );
211 } else {
212 println!(
213 "{}",
214 format!(
215 "⚠️ {} already exists. Skipping.",
216 http_client_path.display()
217 )
218 .yellow()
219 );
220 }
221
222 println!();
223 println!("{}", "✨ Project initialized successfully!".bright_green());
224 println!();
225 println!("Next steps:");
226 println!(" 1. Run: vika-cli generate --spec <path-or-url-to-swagger>");
227 println!(" 2. Select the modules you want to generate");
228
229 Ok(())
230}