use crate::analysis::CommandAnalyzer;
use crate::generators::generator::BindingsGenerator;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Serialize, Deserialize)]
pub struct GenerateConfig {
pub project_path: String,
pub output_path: String,
pub validation_library: String,
pub verbose: Option<bool>,
}
impl Default for GenerateConfig {
fn default() -> Self {
Self {
project_path: "./src-tauri".to_string(),
output_path: "./src/generated".to_string(),
validation_library: "zod".to_string(),
verbose: Some(false),
}
}
}
pub fn generate_from_config(
config: &GenerateConfig,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let verbose = config.verbose.unwrap_or(false);
if verbose {
println!("đ Analyzing Tauri commands in: {}", config.project_path);
}
let project_path = Path::new(&config.project_path);
if !project_path.exists() {
return Err(format!("Project path does not exist: {}", config.project_path).into());
}
let mut analyzer = CommandAnalyzer::new();
let commands = analyzer.analyze_project(&config.project_path)?;
if verbose {
println!("đ Found {} Tauri commands:", commands.len());
commands.iter().for_each(|cmd| {
println!(" - {} ({})", cmd.name, cmd.file_path);
});
let discovered_structs = analyzer.get_discovered_structs();
println!("đī¸ Found {} struct definitions:", discovered_structs.len());
discovered_structs.iter().for_each(|(name, struct_info)| {
let struct_type = if struct_info.is_enum { "enum" } else { "struct" };
println!(
" - {} ({}) with {} fields",
name,
struct_type,
struct_info.fields.len()
);
struct_info.fields.iter().for_each(|field| {
let visibility = if field.is_public { "pub" } else { "private" };
let optional = if field.is_optional { "?" } else { "" };
println!(
" âĸ {}{}: {} ({})",
field.name, optional, field.typescript_type, visibility
);
});
});
if discovered_structs.is_empty() {
println!(" âšī¸ No custom struct definitions found in the project");
}
}
if commands.is_empty() {
if verbose {
println!("â ī¸ No Tauri commands found. Make sure your project contains functions with #[tauri::command] attributes.");
}
return Ok(vec![]);
}
let validation = match config.validation_library.as_str() {
"zod" | "none" => Some(config.validation_library.clone()),
_ => {
return Err("Invalid validation library. Use 'zod' or 'none'".into());
}
};
if verbose {
println!(
"đ Generating TypeScript models with {} validation...",
validation.as_ref().unwrap()
);
}
let mut generator = BindingsGenerator::new(validation);
let generated_files = generator.generate_models(
&commands,
analyzer.get_discovered_structs(),
&config.output_path,
&analyzer,
)?;
if verbose {
println!(
"â
Successfully generated {} files for {} commands:",
generated_files.len(),
commands.len()
);
generated_files.iter().for_each(|file| {
println!(" đ {}/{}", config.output_path, file);
});
}
Ok(generated_files)
}