vika_cli/commands/
generate.rs1use crate::config::loader::{load_config, save_config};
2use crate::config::validator::validate_config;
3use crate::error::Result;
4use crate::progress::ProgressReporter;
5use colored::*;
6use std::path::PathBuf;
7use tabled::Tabled;
8
9#[allow(clippy::too_many_arguments)]
10pub async fn run(
11 _spec: Option<String>,
12 all_specs: bool,
13 spec_name: Option<String>,
14 verbose: bool,
15 cache: bool,
16 backup: bool,
17 force: bool,
18 react_query: bool,
19 swr: bool,
20) -> Result<()> {
21 let hook_flags_count = [react_query, swr].iter().filter(|&&f| f).count();
23 if hook_flags_count > 1 {
24 return Err(crate::error::GenerationError::InvalidHookFlags.into());
25 }
26
27 let mut progress = ProgressReporter::new(verbose);
28
29 progress.success("Starting code generation...");
30 println!();
31
32 progress.start_spinner("Loading configuration...");
34 let mut config = load_config()?;
35 validate_config(&config)?;
36 progress.finish_spinner("Configuration loaded");
37
38 use crate::specs::manager::resolve_spec_selection;
39 use crate::specs::runner::{run_all_specs, run_single_spec, GenerateOptions};
40
41 let specs_to_generate = resolve_spec_selection(&config, spec_name.clone(), all_specs)?;
43
44 use crate::generator::writer::{ensure_directory, write_runtime_client};
46 let root_dir_path = PathBuf::from(&config.root_dir);
47 ensure_directory(&root_dir_path)?;
48 let runtime_dir = root_dir_path.join("runtime");
49 if !runtime_dir.exists() {
50 let apis_config = config.specs.first().map(|s| &s.apis);
52 write_runtime_client(&root_dir_path, None, apis_config)?;
53 if verbose {
54 progress.success("Created runtime client files");
55 }
56 }
57
58 let hook_type = if react_query {
60 Some(crate::specs::runner::HookType::ReactQuery)
61 } else if swr {
62 Some(crate::specs::runner::HookType::Swr)
63 } else {
64 config.specs.iter().find_map(|spec| {
68 spec.hooks
69 .as_ref()
70 .and_then(|h| h.library.as_ref())
71 .and_then(|lib| match lib.as_str() {
72 "react-query" => Some(crate::specs::runner::HookType::ReactQuery),
73 "swr" => Some(crate::specs::runner::HookType::Swr),
74 _ => None,
75 })
76 })
77 };
78
79 let options = GenerateOptions {
80 use_cache: if cache {
81 true
82 } else {
83 config.generation.enable_cache
84 },
85 use_backup: if backup {
86 true
87 } else {
88 config.generation.enable_backup
89 },
90 use_force: if force {
91 true
92 } else {
93 config.generation.conflict_strategy == "force"
94 },
95 verbose,
96 hook_type,
97 };
98
99 if specs_to_generate.len() > 1 {
100 progress.success("Starting multi-spec generation...");
102 println!();
103
104 let stats = run_all_specs(&specs_to_generate, &config, &options).await?;
105
106 for stat in &stats {
108 if let Some(spec_entry) = config.specs.iter_mut().find(|s| s.name == stat.spec_name) {
109 spec_entry.modules.selected = stat.modules.clone();
110 }
111 }
112 save_config(&config)?;
113
114 println!();
115 progress.success(&format!(
116 "Successfully generated code for {} spec(s)!",
117 stats.len()
118 ));
119 println!();
120
121 use tabled::{Table, Tabled};
123 #[derive(Tabled)]
124 struct SpecSummary {
125 #[tabled(rename = "Spec")]
126 spec: String,
127 #[tabled(rename = "Modules")]
128 modules: usize,
129 #[tabled(rename = "Files")]
130 files: usize,
131 }
132
133 let table_data: Vec<SpecSummary> = stats
134 .iter()
135 .map(|s| SpecSummary {
136 spec: s.spec_name.clone(),
137 modules: s.modules_generated,
138 files: s.files_generated,
139 })
140 .collect();
141
142 let table = Table::new(table_data);
143 println!("{}", "Generation summary:".bright_cyan());
144 println!("{}", table);
145 println!();
146 } else {
147 let spec_entry = &specs_to_generate[0];
149 let stats = run_single_spec(spec_entry, &config, &options).await?;
150
151 if let Some(spec_entry) = config.specs.iter_mut().find(|s| s.name == stats.spec_name) {
153 spec_entry.modules.selected = stats.modules.clone();
154 }
155 save_config(&config)?;
156
157 println!();
158 progress.success(&format!(
159 "Successfully generated {} files for spec '{}'!",
160 stats.files_generated, stats.spec_name
161 ));
162 println!();
163
164 let schemas_config = &spec_entry.schemas;
166 let apis_config = &spec_entry.apis;
167
168 println!("{}", "Generated files:".bright_cyan());
169 println!(" 📁 Schemas: {}", schemas_config.output);
170 println!(" 📁 APIs: {}", apis_config.output);
171 println!();
172 }
173
174 Ok(())
175}