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;
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        )?;
78
79        // Generate Zod schemas for common schemas (using same registry)
80        let common_zod_schemas = generate_zod_schemas_with_registry(
81            &parsed.openapi,
82            &parsed.schemas,
83            &common_schemas,
84            &mut shared_enum_registry,
85        )?;
86
87        // Write common schemas
88        let common_files =
89            write_schemas(&schemas_dir, "common", &common_types, &common_zod_schemas)?;
90        total_files += common_files.len();
91        module_summary.push(("common".to_string(), common_files.len()));
92    }
93
94    for module in &selected_modules {
95        println!(
96            "{}",
97            format!("🔨 Regenerating code for module: {}", module).bright_cyan()
98        );
99
100        // Get operations for this module
101        let operations = parsed
102            .operations_by_tag
103            .get(module)
104            .cloned()
105            .unwrap_or_default();
106
107        if operations.is_empty() {
108            println!(
109                "{}",
110                format!("⚠️  No operations found for module: {}", module).yellow()
111            );
112            continue;
113        }
114
115        // Get schema names used by this module (from filtered schemas)
116        let module_schema_names = filtered_module_schemas
117            .get(module)
118            .cloned()
119            .unwrap_or_default();
120
121        // Shared enum registry to ensure consistent naming between TypeScript and Zod
122        let mut shared_enum_registry = std::collections::HashMap::new();
123
124        // Generate TypeScript typings
125        let types = if !module_schema_names.is_empty() {
126            generate_typings_with_registry(
127                &parsed.openapi,
128                &parsed.schemas,
129                &module_schema_names,
130                &mut shared_enum_registry,
131            )?
132        } else {
133            Vec::new()
134        };
135
136        // Generate Zod schemas (using same registry)
137        let zod_schemas = if !module_schema_names.is_empty() {
138            generate_zod_schemas_with_registry(
139                &parsed.openapi,
140                &parsed.schemas,
141                &module_schema_names,
142                &mut shared_enum_registry,
143            )?
144        } else {
145            Vec::new()
146        };
147
148        // Generate API client
149        let api_functions =
150            generate_api_client(&parsed.openapi, &operations, module, &common_schemas)?;
151
152        // Write schemas
153        let schema_files = write_schemas(&schemas_dir, module, &types, &zod_schemas)?;
154        total_files += schema_files.len();
155
156        // Write API client
157        let api_files = write_api_client(&apis_dir, module, &api_functions)?;
158        total_files += api_files.len();
159
160        let module_file_count = schema_files.len() + api_files.len();
161        module_summary.push((module.clone(), module_file_count));
162        println!(
163            "{}",
164            format!(
165                "✅ Regenerated {} files for module: {}",
166                module_file_count, module
167            )
168            .green()
169        );
170    }
171
172    println!();
173    println!(
174        "{}",
175        format!("✨ Successfully updated {} files!", total_files).bright_green()
176    );
177    println!();
178    println!("{}", "Updated files:".bright_cyan());
179    println!("  📁 Schemas: {}", config.schemas.output);
180    println!("  📁 APIs: {}", config.apis.output);
181    println!();
182    if !module_summary.is_empty() {
183        println!("{}", "Module breakdown:".bright_cyan());
184        for (module, count) in &module_summary {
185            println!("  • {}: {} files", module, count);
186        }
187    }
188
189    Ok(())
190}