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