Skip to main content

tauri_typegen/interface/
cli.rs

1use crate::interface::config::GenerateConfig;
2use clap::{Args, Parser, Subcommand};
3use std::path::PathBuf;
4
5#[derive(Parser)]
6#[command(name = "cargo")]
7#[command(bin_name = "cargo")]
8pub struct CargoCli {
9    #[command(subcommand)]
10    pub command: CargoSubcommands,
11}
12
13#[derive(Subcommand)]
14pub enum CargoSubcommands {
15    #[command(name = "tauri-typegen")]
16    TauriTypegen(TauriTypegenArgs),
17}
18
19#[derive(Args)]
20pub struct TauriTypegenArgs {
21    /// Print version information
22    #[arg(short = 'V', long = "version")]
23    pub version: bool,
24
25    #[command(subcommand)]
26    pub command: Option<TypegenCommands>,
27}
28
29#[derive(Subcommand)]
30pub enum TypegenCommands {
31    /// Generate TypeScript models and bindings from Tauri commands
32    Generate {
33        /// Path to the Tauri project source directory. Defaults to config file value or "./src-tauri"
34        #[arg(short = 'p', long = "project-path")]
35        project_path: Option<PathBuf>,
36
37        /// Output path for generated TypeScript files. Defaults to config file value or "./src/generated"
38        #[arg(short = 'o', long = "output-path")]
39        output_path: Option<PathBuf>,
40
41        /// Validation library to use (zod or none). Defaults to config file value or "none"
42        #[arg(short = 'v', long = "validation")]
43        validation_library: Option<String>,
44
45        /// Verbose output
46        #[arg(long, action = clap::ArgAction::SetTrue)]
47        verbose: bool,
48
49        /// Generate dependency graph visualization
50        #[arg(long, action = clap::ArgAction::SetTrue)]
51        visualize_deps: bool,
52
53        /// Configuration file path
54        #[arg(short = 'c', long = "config")]
55        config_file: Option<PathBuf>,
56
57        /// Force regeneration, ignoring cache
58        #[arg(short = 'f', long, action = clap::ArgAction::SetTrue)]
59        force: bool,
60    },
61    /// Initialize configuration for a Tauri project and run initial generation
62    Init {
63        /// Path to the Tauri project source directory. Defaults to "./src-tauri"
64        #[arg(short = 'p', long = "project-path")]
65        project_path: Option<PathBuf>,
66
67        /// Output path for generated TypeScript files. Defaults to "./src/generated"
68        #[arg(short = 'g', long = "generated-path")]
69        generated_path: Option<PathBuf>,
70
71        /// Output path for configuration file. Defaults to "tauri.conf.json"
72        #[arg(short = 'o', long = "output")]
73        output_path: Option<PathBuf>,
74
75        /// Validation library to use (zod or none). Defaults to "none"
76        #[arg(short = 'v', long = "validation")]
77        validation_library: Option<String>,
78
79        /// Verbose output
80        #[arg(long, action = clap::ArgAction::SetTrue)]
81        verbose: bool,
82
83        /// Generate dependency graph visualization
84        #[arg(long, action = clap::ArgAction::SetTrue)]
85        visualize_deps: bool,
86
87        /// Force overwrite existing configuration
88        #[arg(long, action = clap::ArgAction::SetTrue)]
89        force: bool,
90    },
91}
92
93impl From<&TypegenCommands> for GenerateConfig {
94    fn from(cmd: &TypegenCommands) -> Self {
95        match cmd {
96            TypegenCommands::Generate {
97                project_path,
98                output_path,
99                validation_library,
100                verbose,
101                visualize_deps,
102                ..
103            } => {
104                let mut config = GenerateConfig::default();
105                if let Some(p) = project_path {
106                    config.project_path = p.to_string_lossy().to_string();
107                }
108                if let Some(o) = output_path {
109                    config.output_path = o.to_string_lossy().to_string();
110                }
111                if let Some(v) = validation_library {
112                    config.validation_library = v.clone();
113                }
114                // For boolean flags: only set if true (flag was present)
115                if *verbose {
116                    config.verbose = Some(true);
117                }
118                if *visualize_deps {
119                    config.visualize_deps = Some(true);
120                }
121                config
122            }
123            TypegenCommands::Init {
124                project_path,
125                generated_path,
126                validation_library,
127                verbose,
128                visualize_deps,
129                ..
130            } => {
131                let mut config = GenerateConfig::default();
132                if let Some(p) = project_path {
133                    config.project_path = p.to_string_lossy().to_string();
134                }
135                if let Some(g) = generated_path {
136                    config.output_path = g.to_string_lossy().to_string();
137                }
138                if let Some(v) = validation_library {
139                    config.validation_library = v.clone();
140                }
141                // For boolean flags: only set if true (flag was present)
142                if *verbose {
143                    config.verbose = Some(true);
144                }
145                if *visualize_deps {
146                    config.visualize_deps = Some(true);
147                }
148                config
149            }
150        }
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn test_default_generate_config_from_cli() {
160        // When no CLI args are provided, should use defaults
161        let cmd = TypegenCommands::Generate {
162            project_path: None,
163            output_path: None,
164            validation_library: None,
165            verbose: false,
166            visualize_deps: false,
167            config_file: None,
168            force: false,
169        };
170
171        let config = GenerateConfig::from(&cmd);
172        assert_eq!(config.project_path, "./src-tauri");
173        assert_eq!(config.output_path, "./src/generated");
174        assert_eq!(config.validation_library, "none");
175        assert_eq!(config.verbose, Some(false));
176        assert_eq!(config.visualize_deps, Some(false));
177    }
178
179    #[test]
180    fn test_custom_generate_config_from_cli() {
181        let cmd = TypegenCommands::Generate {
182            project_path: Some(PathBuf::from("./my-tauri")),
183            output_path: Some(PathBuf::from("./types")),
184            validation_library: Some("none".to_string()),
185            verbose: true,
186            visualize_deps: true,
187            config_file: None,
188            force: false,
189        };
190
191        let config = GenerateConfig::from(&cmd);
192        assert_eq!(config.project_path, "./my-tauri");
193        assert_eq!(config.output_path, "./types");
194        assert_eq!(config.validation_library, "none");
195        assert_eq!(config.verbose, Some(true));
196        assert_eq!(config.visualize_deps, Some(true));
197    }
198
199    #[test]
200    fn test_partial_generate_config_from_cli() {
201        // Test that only specified values override defaults
202        let cmd = TypegenCommands::Generate {
203            project_path: None,
204            output_path: None,
205            validation_library: Some("none".to_string()),
206            verbose: true,
207            visualize_deps: false,
208            config_file: None,
209            force: false,
210        };
211
212        let config = GenerateConfig::from(&cmd);
213        assert_eq!(config.project_path, "./src-tauri"); // default
214        assert_eq!(config.output_path, "./src/generated"); // default
215        assert_eq!(config.validation_library, "none"); // overridden
216        assert_eq!(config.verbose, Some(true)); // overridden
217        assert_eq!(config.visualize_deps, Some(false)); // default (not set)
218    }
219
220    #[test]
221    fn test_default_init_config_from_cli() {
222        let cmd = TypegenCommands::Init {
223            project_path: None,
224            generated_path: None,
225            output_path: None,
226            validation_library: None,
227            verbose: false,
228            visualize_deps: false,
229            force: false,
230        };
231
232        let config = GenerateConfig::from(&cmd);
233        assert_eq!(config.project_path, "./src-tauri");
234        assert_eq!(config.output_path, "./src/generated");
235        assert_eq!(config.validation_library, "none");
236        assert_eq!(config.verbose, Some(false));
237        assert_eq!(config.visualize_deps, Some(false));
238    }
239
240    #[test]
241    fn test_custom_init_config_from_cli() {
242        let cmd = TypegenCommands::Init {
243            project_path: Some(PathBuf::from("./my-tauri")),
244            generated_path: Some(PathBuf::from("./my-types")),
245            output_path: Some(PathBuf::from("custom.conf.json")),
246            validation_library: Some("none".to_string()),
247            verbose: true,
248            visualize_deps: true,
249            force: true,
250        };
251
252        let config = GenerateConfig::from(&cmd);
253        assert_eq!(config.project_path, "./my-tauri");
254        assert_eq!(config.output_path, "./my-types");
255        assert_eq!(config.validation_library, "none");
256        assert_eq!(config.verbose, Some(true));
257        assert_eq!(config.visualize_deps, Some(true));
258    }
259}