vika_cli/commands/
update.rs

1use crate::config::loader::load_config;
2use crate::config::validator::validate_config;
3use crate::error::Result;
4use crate::generator::api_client::generate_api_client_with_registry;
5use crate::generator::swagger_parser::{fetch_and_parse_spec, filter_common_schemas};
6use crate::generator::ts_typings::generate_typings_with_registry;
7use crate::generator::writer::{write_api_client, write_schemas};
8use crate::generator::zod_schema::generate_zod_schemas_with_registry;
9use colored::*;
10use std::path::PathBuf;
11
12pub async fn run() -> Result<()> {
13    println!("{}", "🔄 Updating generated code...".bright_cyan());
14    println!();
15
16    // Load config
17    let config = load_config()?;
18    validate_config(&config)?;
19
20    use crate::error::GenerationError;
21
22    // Get spec path from config
23    let spec_path = config.spec_path.ok_or(GenerationError::SpecPathRequired)?;
24
25    // Get selected modules from config
26    let selected_modules = if config.modules.selected.is_empty() {
27        return Err(GenerationError::NoModulesSelected.into());
28    } else {
29        config.modules.selected.clone()
30    };
31
32    println!(
33        "{}",
34        format!("📥 Fetching spec from: {}", spec_path).bright_blue()
35    );
36    let parsed = fetch_and_parse_spec(&spec_path).await?;
37    println!(
38        "{}",
39        format!("✅ Parsed spec with {} modules", parsed.modules.len()).green()
40    );
41    println!();
42    println!(
43        "{}",
44        format!(
45            "📦 Updating {} module(s): {}",
46            selected_modules.len(),
47            selected_modules.join(", ")
48        )
49        .bright_green()
50    );
51    println!();
52
53    // Filter common schemas based on selected modules only
54    let (filtered_module_schemas, common_schemas) =
55        filter_common_schemas(&parsed.module_schemas, &selected_modules);
56
57    // Generate code for each module
58    let schemas_dir = PathBuf::from(&config.schemas.output);
59    let apis_dir = PathBuf::from(&config.apis.output);
60
61    let mut total_files = 0;
62    let mut module_summary: Vec<(String, usize)> = Vec::new();
63
64    // Generate common module first if there are shared schemas
65    if !common_schemas.is_empty() {
66        println!("{}", "🔨 Regenerating common schemas...".bright_cyan());
67
68        // Shared enum registry to ensure consistent naming between TypeScript and Zod
69        let mut shared_enum_registry = std::collections::HashMap::new();
70
71        // Generate TypeScript typings for common schemas
72        let common_types = generate_typings_with_registry(
73            &parsed.openapi,
74            &parsed.schemas,
75            &common_schemas,
76            &mut shared_enum_registry,
77            &common_schemas,
78        )?;
79
80        // Generate Zod schemas for common schemas (using same registry)
81        let common_zod_schemas = generate_zod_schemas_with_registry(
82            &parsed.openapi,
83            &parsed.schemas,
84            &common_schemas,
85            &mut shared_enum_registry,
86            &common_schemas,
87        )?;
88
89        // Write common schemas
90        let common_files =
91            write_schemas(&schemas_dir, "common", &common_types, &common_zod_schemas)?;
92        total_files += common_files.len();
93        module_summary.push(("common".to_string(), common_files.len()));
94    }
95
96    for module in &selected_modules {
97        println!(
98            "{}",
99            format!("🔨 Regenerating code for module: {}", module).bright_cyan()
100        );
101
102        // Get operations for this module
103        let operations = parsed
104            .operations_by_tag
105            .get(module)
106            .cloned()
107            .unwrap_or_default();
108
109        if operations.is_empty() {
110            println!(
111                "{}",
112                format!("⚠️  No operations found for module: {}", module).yellow()
113            );
114            continue;
115        }
116
117        // Get schema names used by this module (from filtered schemas)
118        let module_schema_names = filtered_module_schemas
119            .get(module)
120            .cloned()
121            .unwrap_or_default();
122
123        // Shared enum registry to ensure consistent naming between TypeScript and Zod
124        let mut shared_enum_registry = std::collections::HashMap::new();
125
126        // Generate TypeScript typings
127        let types = if !module_schema_names.is_empty() {
128            generate_typings_with_registry(
129                &parsed.openapi,
130                &parsed.schemas,
131                &module_schema_names,
132                &mut shared_enum_registry,
133                &common_schemas,
134            )?
135        } else {
136            Vec::new()
137        };
138
139        // Generate Zod schemas (using same registry)
140        let zod_schemas = if !module_schema_names.is_empty() {
141            generate_zod_schemas_with_registry(
142                &parsed.openapi,
143                &parsed.schemas,
144                &module_schema_names,
145                &mut shared_enum_registry,
146                &common_schemas,
147            )?
148        } else {
149            Vec::new()
150        };
151
152        // Generate API client (using same enum registry as schemas)
153        let api_result = generate_api_client_with_registry(
154            &parsed.openapi,
155            &operations,
156            module,
157            &common_schemas,
158            &mut shared_enum_registry,
159        )?;
160
161        // Combine response types with schema types
162        let mut all_types = types;
163        all_types.extend(api_result.response_types);
164
165        // Write schemas
166        let schema_files = write_schemas(&schemas_dir, module, &all_types, &zod_schemas)?;
167        total_files += schema_files.len();
168
169        // Write API client
170        let api_files = write_api_client(&apis_dir, module, &api_result.functions)?;
171        total_files += api_files.len();
172
173        let module_file_count = schema_files.len() + api_files.len();
174        module_summary.push((module.clone(), module_file_count));
175        println!(
176            "{}",
177            format!(
178                "✅ Regenerated {} files for module: {}",
179                module_file_count, module
180            )
181            .green()
182        );
183    }
184
185    println!();
186    println!(
187        "{}",
188        format!("✨ Successfully updated {} files!", total_files).bright_green()
189    );
190    println!();
191    println!("{}", "Updated files:".bright_cyan());
192    println!("  📁 Schemas: {}", config.schemas.output);
193    println!("  📁 APIs: {}", config.apis.output);
194    println!();
195    if !module_summary.is_empty() {
196        println!("{}", "Module breakdown:".bright_cyan());
197        for (module, count) in &module_summary {
198            println!("  • {}: {} files", module, count);
199        }
200    }
201
202    Ok(())
203}