tauri_typegen/interface/
mod.rs

1pub mod cli;
2pub mod config;
3pub mod output;
4
5use crate::analysis::CommandAnalyzer;
6use crate::generators::create_generator;
7
8pub use cli::*;
9pub use config::*;
10pub use output::*;
11
12/// Generate TypeScript bindings from a Tauri project.
13///
14/// This is the main entry point for programmatic generation of TypeScript bindings.
15/// It analyzes Rust source code in the specified project, discovers Tauri commands,
16/// and generates TypeScript interfaces and command functions.
17///
18/// # Arguments
19///
20/// * `config` - Configuration specifying project paths, output location, and validation options
21///
22/// # Returns
23///
24/// Returns a list of generated file paths on success.
25///
26/// # Errors
27///
28/// Returns an error if:
29/// - Configuration is invalid
30/// - Project directory cannot be read
31/// - Rust code has syntax errors
32/// - Output directory cannot be written to
33///
34/// # Example
35///
36/// ```rust,no_run
37/// use tauri_typegen::{GenerateConfig, generate_from_config};
38///
39/// let config = GenerateConfig {
40///     project_path: "./src-tauri".to_string(),
41///     output_path: "./src/generated".to_string(),
42///     validation_library: "zod".to_string(),
43///     verbose: Some(true),
44///     ..Default::default()
45/// };
46///
47/// let files = generate_from_config(&config)?;
48/// println!("Generated {} files", files.len());
49/// # Ok::<(), Box<dyn std::error::Error>>(())
50/// ```
51pub fn generate_from_config(
52    config: &config::GenerateConfig,
53) -> Result<Vec<String>, Box<dyn std::error::Error>> {
54    let logger = output::Logger::new(config.is_verbose(), false);
55
56    if config.is_verbose() {
57        logger.info(&format!(
58            "🔍 Analyzing Tauri commands in: {}",
59            config.project_path
60        ));
61    }
62
63    // Validate configuration
64    config
65        .validate()
66        .map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
67
68    // Analyze commands with struct discovery
69    let mut analyzer = CommandAnalyzer::new();
70
71    // Apply custom type mappings from configuration
72    if let Some(ref mappings) = config.type_mappings {
73        analyzer.add_type_mappings(mappings);
74        if config.is_verbose() {
75            logger.info(&format!(
76                "📝 Applied {} custom type mappings",
77                mappings.len()
78            ));
79            for (rust_type, ts_type) in mappings {
80                logger.verbose(&format!("  {} → {}", rust_type, ts_type));
81            }
82        }
83    }
84
85    let commands = analyzer.analyze_project(&config.project_path)?;
86
87    if config.is_verbose() {
88        logger.info(&format!("📋 Found {} Tauri commands:", commands.len()));
89        for cmd in &commands {
90            logger.verbose(&format!("  - {} ({})", cmd.name, cmd.file_path));
91        }
92
93        let discovered_structs = analyzer.get_discovered_structs();
94        logger.info(&format!(
95            "đŸ—ī¸  Found {} struct definitions:",
96            discovered_structs.len()
97        ));
98        for (name, struct_info) in discovered_structs {
99            let struct_type = if struct_info.is_enum {
100                "enum"
101            } else {
102                "struct"
103            };
104            logger.verbose(&format!(
105                "  - {} ({}) with {} fields",
106                name,
107                struct_type,
108                struct_info.fields.len()
109            ));
110            for field in &struct_info.fields {
111                let visibility = if field.is_public { "pub" } else { "private" };
112                let optional = if field.is_optional { "?" } else { "" };
113                logger.verbose(&format!(
114                    "    â€ĸ {}{}: {} ({})",
115                    field.name, optional, field.rust_type, visibility
116                ));
117            }
118        }
119
120        if discovered_structs.is_empty() {
121            logger.info("  â„šī¸  No custom struct definitions found in the project");
122        }
123    }
124
125    if commands.is_empty() {
126        if config.is_verbose() {
127            logger.warning("âš ī¸  No Tauri commands found. Make sure your project contains functions with #[tauri::command] attributes.");
128        }
129        return Ok(vec![]);
130    }
131
132    // Validate validation library
133    let validation = match config.validation_library.as_str() {
134        "zod" | "none" => Some(config.validation_library.clone()),
135        _ => {
136            return Err("Invalid validation library. Use 'zod' or 'none'".into());
137        }
138    };
139
140    if config.is_verbose() {
141        logger.info(&format!(
142            "🚀 Generating TypeScript models with {} validation...",
143            validation.as_ref().unwrap()
144        ));
145    }
146
147    // Generate TypeScript models with discovered structs
148    let mut generator = create_generator(validation);
149    let generated_files = generator.generate_models(
150        &commands,
151        analyzer.get_discovered_structs(),
152        &config.output_path,
153        &analyzer,
154        config,
155    )?;
156
157    if config.is_verbose() {
158        logger.info(&format!(
159            "✅ Successfully generated {} files for {} commands:",
160            generated_files.len(),
161            commands.len()
162        ));
163        for file in &generated_files {
164            logger.verbose(&format!("  📄 {}/{}", config.output_path, file));
165        }
166    }
167
168    Ok(generated_files)
169}