use crate::config::loader::{load_config, save_config};
use crate::config::validator::validate_config;
use crate::error::Result;
use crate::progress::ProgressReporter;
use colored::*;
use std::path::PathBuf;
use tabled::Tabled;
#[allow(clippy::too_many_arguments)]
pub async fn run(
_spec: Option<String>,
all_specs: bool,
spec_name: Option<String>,
verbose: bool,
cache: bool,
backup: bool,
force: bool,
react_query: bool,
swr: bool,
) -> Result<()> {
let hook_flags_count = [react_query, swr].iter().filter(|&&f| f).count();
if hook_flags_count > 1 {
return Err(crate::error::GenerationError::InvalidHookFlags.into());
}
let mut progress = ProgressReporter::new(verbose);
progress.success("Starting code generation...");
println!();
progress.start_spinner("Loading configuration...");
let mut config = load_config()?;
validate_config(&config)?;
progress.finish_spinner("Configuration loaded");
use crate::specs::manager::resolve_spec_selection;
use crate::specs::runner::{run_all_specs, run_single_spec, GenerateOptions};
let specs_to_generate = resolve_spec_selection(&config, spec_name.clone(), all_specs)?;
use crate::generator::writer::{ensure_directory, write_runtime_client};
let root_dir_path = PathBuf::from(&config.root_dir);
ensure_directory(&root_dir_path)?;
let runtime_dir = root_dir_path.join("runtime");
if !runtime_dir.exists() {
let apis_config = config.specs.first().map(|s| &s.apis);
write_runtime_client(&root_dir_path, None, apis_config)?;
if verbose {
progress.success("Created runtime client files");
}
}
let hook_type = if react_query {
Some(crate::specs::runner::HookType::ReactQuery)
} else if swr {
Some(crate::specs::runner::HookType::Swr)
} else {
config.specs.iter().find_map(|spec| {
spec.hooks
.as_ref()
.and_then(|h| h.library.as_ref())
.and_then(|lib| match lib.as_str() {
"react-query" => Some(crate::specs::runner::HookType::ReactQuery),
"swr" => Some(crate::specs::runner::HookType::Swr),
_ => None,
})
})
};
let options = GenerateOptions {
use_cache: if cache {
true
} else {
config.generation.enable_cache
},
use_backup: if backup {
true
} else {
config.generation.enable_backup
},
use_force: if force {
true
} else {
config.generation.conflict_strategy == "force"
},
verbose,
hook_type,
};
if specs_to_generate.len() > 1 {
progress.success("Starting multi-spec generation...");
println!();
let stats = run_all_specs(&specs_to_generate, &config, &options).await?;
for stat in &stats {
if let Some(spec_entry) = config.specs.iter_mut().find(|s| s.name == stat.spec_name) {
spec_entry.modules.selected = stat.modules.clone();
}
}
save_config(&config)?;
println!();
progress.success(&format!(
"Successfully generated code for {} spec(s)!",
stats.len()
));
println!();
use tabled::{Table, Tabled};
#[derive(Tabled)]
struct SpecSummary {
#[tabled(rename = "Spec")]
spec: String,
#[tabled(rename = "Modules")]
modules: usize,
#[tabled(rename = "Files")]
files: usize,
}
let table_data: Vec<SpecSummary> = stats
.iter()
.map(|s| SpecSummary {
spec: s.spec_name.clone(),
modules: s.modules_generated,
files: s.files_generated,
})
.collect();
let table = Table::new(table_data);
println!("{}", "Generation summary:".bright_cyan());
println!("{}", table);
println!();
} else {
let spec_entry = &specs_to_generate[0];
let stats = run_single_spec(spec_entry, &config, &options).await?;
if let Some(spec_entry) = config.specs.iter_mut().find(|s| s.name == stats.spec_name) {
spec_entry.modules.selected = stats.modules.clone();
}
save_config(&config)?;
println!();
progress.success(&format!(
"Successfully generated {} files for spec '{}'!",
stats.files_generated, stats.spec_name
));
println!();
let schemas_config = &spec_entry.schemas;
let apis_config = &spec_entry.apis;
println!("{}", "Generated files:".bright_cyan());
println!(" 📁 Schemas: {}", schemas_config.output);
println!(" 📁 APIs: {}", apis_config.output);
println!();
}
Ok(())
}