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    /// Initialize configuration for a Tauri project and run initial generation
58    Init {
59        /// Path to the Tauri project source directory. Defaults to "./src-tauri"
60        #[arg(short = 'p', long = "project-path")]
61        project_path: Option<PathBuf>,
62
63        /// Output path for generated TypeScript files. Defaults to "./src/generated"
64        #[arg(short = 'g', long = "generated-path")]
65        generated_path: Option<PathBuf>,
66
67        /// Output path for configuration file. Defaults to "tauri.conf.json"
68        #[arg(short = 'o', long = "output")]
69        output_path: Option<PathBuf>,
70
71        /// Validation library to use (zod or none). Defaults to "none"
72        #[arg(short = 'v', long = "validation")]
73        validation_library: Option<String>,
74
75        /// Verbose output
76        #[arg(long, action = clap::ArgAction::SetTrue)]
77        verbose: bool,
78
79        /// Generate dependency graph visualization
80        #[arg(long, action = clap::ArgAction::SetTrue)]
81        visualize_deps: bool,
82
83        /// Force overwrite existing configuration
84        #[arg(long, action = clap::ArgAction::SetTrue)]
85        force: bool,
86    },
87}
88
89impl From<&TypegenCommands> for GenerateConfig {
90    fn from(cmd: &TypegenCommands) -> Self {
91        match cmd {
92            TypegenCommands::Generate {
93                project_path,
94                output_path,
95                validation_library,
96                verbose,
97                visualize_deps,
98                ..
99            } => {
100                let mut config = GenerateConfig::default();
101                if let Some(p) = project_path {
102                    config.project_path = p.to_string_lossy().to_string();
103                }
104                if let Some(o) = output_path {
105                    config.output_path = o.to_string_lossy().to_string();
106                }
107                if let Some(v) = validation_library {
108                    config.validation_library = v.clone();
109                }
110                // For boolean flags: only set if true (flag was present)
111                if *verbose {
112                    config.verbose = Some(true);
113                }
114                if *visualize_deps {
115                    config.visualize_deps = Some(true);
116                }
117                config
118            }
119            TypegenCommands::Init {
120                project_path,
121                generated_path,
122                validation_library,
123                verbose,
124                visualize_deps,
125                ..
126            } => {
127                let mut config = GenerateConfig::default();
128                if let Some(p) = project_path {
129                    config.project_path = p.to_string_lossy().to_string();
130                }
131                if let Some(g) = generated_path {
132                    config.output_path = g.to_string_lossy().to_string();
133                }
134                if let Some(v) = validation_library {
135                    config.validation_library = v.clone();
136                }
137                // For boolean flags: only set if true (flag was present)
138                if *verbose {
139                    config.verbose = Some(true);
140                }
141                if *visualize_deps {
142                    config.visualize_deps = Some(true);
143                }
144                config
145            }
146        }
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn test_default_generate_config_from_cli() {
156        // When no CLI args are provided, should use defaults
157        let cmd = TypegenCommands::Generate {
158            project_path: None,
159            output_path: None,
160            validation_library: None,
161            verbose: false,
162            visualize_deps: false,
163            config_file: None,
164        };
165
166        let config = GenerateConfig::from(&cmd);
167        assert_eq!(config.project_path, "./src-tauri");
168        assert_eq!(config.output_path, "./src/generated");
169        assert_eq!(config.validation_library, "none");
170        assert_eq!(config.verbose, Some(false));
171        assert_eq!(config.visualize_deps, Some(false));
172    }
173
174    #[test]
175    fn test_custom_generate_config_from_cli() {
176        let cmd = TypegenCommands::Generate {
177            project_path: Some(PathBuf::from("./my-tauri")),
178            output_path: Some(PathBuf::from("./types")),
179            validation_library: Some("none".to_string()),
180            verbose: true,
181            visualize_deps: true,
182            config_file: None,
183        };
184
185        let config = GenerateConfig::from(&cmd);
186        assert_eq!(config.project_path, "./my-tauri");
187        assert_eq!(config.output_path, "./types");
188        assert_eq!(config.validation_library, "none");
189        assert_eq!(config.verbose, Some(true));
190        assert_eq!(config.visualize_deps, Some(true));
191    }
192
193    #[test]
194    fn test_partial_generate_config_from_cli() {
195        // Test that only specified values override defaults
196        let cmd = TypegenCommands::Generate {
197            project_path: None,
198            output_path: None,
199            validation_library: Some("none".to_string()),
200            verbose: true,
201            visualize_deps: false,
202            config_file: None,
203        };
204
205        let config = GenerateConfig::from(&cmd);
206        assert_eq!(config.project_path, "./src-tauri"); // default
207        assert_eq!(config.output_path, "./src/generated"); // default
208        assert_eq!(config.validation_library, "none"); // overridden
209        assert_eq!(config.verbose, Some(true)); // overridden
210        assert_eq!(config.visualize_deps, Some(false)); // default (not set)
211    }
212
213    #[test]
214    fn test_default_init_config_from_cli() {
215        let cmd = TypegenCommands::Init {
216            project_path: None,
217            generated_path: None,
218            output_path: None,
219            validation_library: None,
220            verbose: false,
221            visualize_deps: false,
222            force: false,
223        };
224
225        let config = GenerateConfig::from(&cmd);
226        assert_eq!(config.project_path, "./src-tauri");
227        assert_eq!(config.output_path, "./src/generated");
228        assert_eq!(config.validation_library, "none");
229        assert_eq!(config.verbose, Some(false));
230        assert_eq!(config.visualize_deps, Some(false));
231    }
232
233    #[test]
234    fn test_custom_init_config_from_cli() {
235        let cmd = TypegenCommands::Init {
236            project_path: Some(PathBuf::from("./my-tauri")),
237            generated_path: Some(PathBuf::from("./my-types")),
238            output_path: Some(PathBuf::from("custom.conf.json")),
239            validation_library: Some("none".to_string()),
240            verbose: true,
241            visualize_deps: true,
242            force: true,
243        };
244
245        let config = GenerateConfig::from(&cmd);
246        assert_eq!(config.project_path, "./my-tauri");
247        assert_eq!(config.output_path, "./my-types");
248        assert_eq!(config.validation_library, "none");
249        assert_eq!(config.verbose, Some(true));
250        assert_eq!(config.visualize_deps, Some(true));
251    }
252}