1use crate::{
7 Command, CommandBuilder, CommandCategory, CommandProvider, ProjectContext, ProjectType,
8 RazResult, SymbolKind,
9};
10use async_trait::async_trait;
11
12pub struct TauriProvider {
14 priority: u8,
15}
16
17impl TauriProvider {
18 pub fn new() -> Self {
19 Self {
20 priority: 88, }
22 }
23}
24
25#[async_trait]
26impl CommandProvider for TauriProvider {
27 fn name(&self) -> &str {
28 "tauri"
29 }
30
31 fn priority(&self) -> u8 {
32 self.priority
33 }
34
35 fn can_handle(&self, context: &ProjectContext) -> bool {
36 match &context.project_type {
38 ProjectType::Tauri => true,
39 ProjectType::Mixed(frameworks) => frameworks.contains(&ProjectType::Tauri),
40 _ => {
41 context.dependencies.iter().any(|dep|
43 dep.name == "tauri" ||
44 dep.name == "tauri-build" ||
45 dep.name.starts_with("tauri-")
46 ) ||
47 context.workspace_root.join("src-tauri").exists() ||
49 context.workspace_root.join("tauri.conf.json").exists()
50 }
51 }
52 }
53
54 async fn commands(&self, context: &ProjectContext) -> RazResult<Vec<Command>> {
55 let mut commands = Vec::new();
56
57 commands.extend(self.development_commands(context));
59
60 commands.extend(self.build_commands(context));
62
63 commands.extend(self.test_commands(context));
65
66 commands.extend(self.context_aware_commands(context));
68
69 commands.extend(self.distribution_commands(context));
71
72 Ok(commands)
73 }
74}
75
76impl TauriProvider {
77 fn development_commands(&self, _context: &ProjectContext) -> Vec<Command> {
79 vec![
80 CommandBuilder::new("tauri-dev", "cargo")
81 .label("Tauri Dev Server")
82 .description("Start Tauri development server with hot reload")
83 .arg("tauri")
84 .arg("dev")
85 .category(CommandCategory::Run)
86 .priority(95)
87 .tag("dev")
88 .tag("serve")
89 .tag("tauri")
90 .estimated_duration(8)
91 .build(),
92 CommandBuilder::new("tauri-dev-debug", "cargo")
93 .label("Tauri Debug Mode")
94 .description("Start development with debugging enabled")
95 .arg("tauri")
96 .arg("dev")
97 .arg("--debug")
98 .category(CommandCategory::Run)
99 .priority(90)
100 .tag("dev")
101 .tag("debug")
102 .tag("tauri")
103 .estimated_duration(10)
104 .build(),
105 CommandBuilder::new("tauri-dev-release", "cargo")
106 .label("Tauri Dev Release")
107 .description("Run in development mode with release build")
108 .arg("tauri")
109 .arg("dev")
110 .arg("--release")
111 .category(CommandCategory::Run)
112 .priority(85)
113 .tag("dev")
114 .tag("release")
115 .tag("tauri")
116 .estimated_duration(30)
117 .build(),
118 CommandBuilder::new("tauri-dev-no-watch", "cargo")
119 .label("Tauri Dev (No Watch)")
120 .description("Start development without file watching")
121 .arg("tauri")
122 .arg("dev")
123 .arg("--no-watch")
124 .category(CommandCategory::Run)
125 .priority(80)
126 .tag("dev")
127 .tag("no-watch")
128 .tag("tauri")
129 .estimated_duration(6)
130 .build(),
131 CommandBuilder::new("tauri-info", "cargo")
132 .label("Tauri System Info")
133 .description("Show Tauri environment and system information")
134 .arg("tauri")
135 .arg("info")
136 .category(CommandCategory::Generate)
137 .priority(75)
138 .tag("info")
139 .tag("system")
140 .tag("tauri")
141 .estimated_duration(2)
142 .build(),
143 CommandBuilder::new("tauri-init", "cargo")
144 .label("Initialize Tauri")
145 .description("Add Tauri to existing project")
146 .arg("tauri")
147 .arg("init")
148 .category(CommandCategory::Generate)
149 .priority(70)
150 .tag("init")
151 .tag("setup")
152 .tag("tauri")
153 .estimated_duration(5)
154 .build(),
155 ]
156 }
157
158 fn build_commands(&self, context: &ProjectContext) -> Vec<Command> {
160 let mut commands = vec![
161 CommandBuilder::new("tauri-build", "cargo")
162 .label("Build Tauri App")
163 .description("Build the Tauri application for current platform")
164 .arg("tauri")
165 .arg("build")
166 .category(CommandCategory::Build)
167 .priority(85)
168 .tag("build")
169 .tag("tauri")
170 .estimated_duration(60)
171 .build(),
172 CommandBuilder::new("tauri-build-debug", "cargo")
173 .label("Build Debug")
174 .description("Build debug version of the app")
175 .arg("tauri")
176 .arg("build")
177 .arg("--debug")
178 .category(CommandCategory::Build)
179 .priority(80)
180 .tag("build")
181 .tag("debug")
182 .tag("tauri")
183 .estimated_duration(45)
184 .build(),
185 ];
186
187 let is_cross_platform = context.dependencies.iter().any(|d| {
189 d.features
190 .iter()
191 .any(|f| f.contains("updater") || f.contains("api-all"))
192 });
193
194 if is_cross_platform {
195 commands.extend(vec![
196 CommandBuilder::new("tauri-build-windows", "cargo")
197 .label("Build for Windows")
198 .description("Cross-compile for Windows")
199 .arg("tauri")
200 .arg("build")
201 .arg("--target")
202 .arg("x86_64-pc-windows-msvc")
203 .category(CommandCategory::Build)
204 .priority(75)
205 .tag("build")
206 .tag("windows")
207 .tag("cross")
208 .tag("tauri")
209 .estimated_duration(90)
210 .build(),
211 CommandBuilder::new("tauri-build-macos", "cargo")
212 .label("Build for macOS")
213 .description("Cross-compile for macOS")
214 .arg("tauri")
215 .arg("build")
216 .arg("--target")
217 .arg("x86_64-apple-darwin")
218 .category(CommandCategory::Build)
219 .priority(75)
220 .tag("build")
221 .tag("macos")
222 .tag("cross")
223 .tag("tauri")
224 .estimated_duration(100)
225 .build(),
226 CommandBuilder::new("tauri-build-linux", "cargo")
227 .label("Build for Linux")
228 .description("Cross-compile for Linux")
229 .arg("tauri")
230 .arg("build")
231 .arg("--target")
232 .arg("x86_64-unknown-linux-gnu")
233 .category(CommandCategory::Build)
234 .priority(75)
235 .tag("build")
236 .tag("linux")
237 .tag("cross")
238 .tag("tauri")
239 .estimated_duration(85)
240 .build(),
241 ]);
242 }
243
244 commands
245 }
246
247 fn test_commands(&self, _context: &ProjectContext) -> Vec<Command> {
249 vec![
250 CommandBuilder::new("tauri-test", "cargo")
251 .label("Run Tauri Tests")
252 .description("Run unit and integration tests")
253 .arg("test")
254 .category(CommandCategory::Test)
255 .priority(75)
256 .tag("test")
257 .tag("tauri")
258 .estimated_duration(25)
259 .build(),
260 CommandBuilder::new("tauri-test-core", "cargo")
261 .label("Test Core Logic")
262 .description("Test Rust core logic without UI")
263 .arg("test")
264 .arg("--package")
265 .arg("src-tauri")
266 .category(CommandCategory::Test)
267 .priority(80)
268 .tag("test")
269 .tag("core")
270 .tag("tauri")
271 .estimated_duration(15)
272 .build(),
273 CommandBuilder::new("tauri-test-webdriver", "cargo")
274 .label("WebDriver Tests")
275 .description("Run end-to-end tests with WebDriver")
276 .arg("tauri")
277 .arg("build")
278 .arg("--debug")
279 .args(vec![
280 "&&".to_string(),
281 "cargo".to_string(),
282 "test".to_string(),
283 "--test".to_string(),
284 "webdriver".to_string(),
285 ])
286 .category(CommandCategory::Test)
287 .priority(70)
288 .tag("test")
289 .tag("e2e")
290 .tag("webdriver")
291 .tag("tauri")
292 .estimated_duration(60)
293 .build(),
294 ]
295 }
296
297 fn context_aware_commands(&self, context: &ProjectContext) -> Vec<Command> {
299 let mut commands = Vec::new();
300
301 if let Some(file_context) = &context.current_file {
302 if let Some(symbol) = &file_context.cursor_symbol {
303 match symbol.kind {
304 SymbolKind::Function => {
305 if symbol.name.starts_with("cmd_")
307 || symbol.name.contains("command")
308 || symbol.modifiers.contains(&"tauri::command".to_string())
309 {
310 commands.push(
311 CommandBuilder::new("tauri-test-command", "cargo")
312 .label("Test Tauri Command")
313 .description(format!("Test command function: {}", symbol.name))
314 .arg("test")
315 .arg(format!("test_{}", symbol.name))
316 .category(CommandCategory::Test)
317 .priority(85)
318 .tag("command")
319 .tag("test")
320 .tag("tauri")
321 .estimated_duration(8)
322 .build(),
323 );
324 }
325
326 if symbol.name.contains("event") || symbol.name.contains("handler") {
328 commands.push(
329 CommandBuilder::new("tauri-dev-events", "cargo")
330 .label("Debug Event Handlers")
331 .description(format!(
332 "Run with event debugging: {}",
333 symbol.name
334 ))
335 .arg("tauri")
336 .arg("dev")
337 .arg("--debug")
338 .category(CommandCategory::Run)
339 .priority(80)
340 .tag("events")
341 .tag("debug")
342 .tag("tauri")
343 .estimated_duration(12)
344 .build(),
345 );
346 }
347
348 if symbol.name.starts_with("test_")
350 || symbol.modifiers.contains(&"test".to_string())
351 {
352 commands.push(
353 CommandBuilder::new("tauri-test-current", "cargo")
354 .label("Test Current Function")
355 .description(format!("Run test: {}", symbol.name))
356 .arg("test")
357 .arg(&symbol.name)
358 .arg("--")
359 .arg("--nocapture")
360 .category(CommandCategory::Test)
361 .priority(90)
362 .tag("test")
363 .tag("current")
364 .tag("tauri")
365 .estimated_duration(5)
366 .build(),
367 );
368 }
369 }
370 SymbolKind::Struct => {
371 if symbol.name.ends_with("State") || symbol.name.contains("Config") {
373 commands.push(
374 CommandBuilder::new("tauri-check-state", "cargo")
375 .label("Check State Management")
376 .description(format!("Validate state struct: {}", symbol.name))
377 .arg("check")
378 .category(CommandCategory::Lint)
379 .priority(75)
380 .tag("state")
381 .tag("check")
382 .tag("tauri")
383 .estimated_duration(10)
384 .build(),
385 );
386 }
387 }
388 _ => {}
389 }
390 }
391
392 if file_context.path.to_string_lossy().contains("main.rs")
394 && file_context.path.to_string_lossy().contains("src-tauri")
395 {
396 commands.push(
397 CommandBuilder::new("tauri-check-main", "cargo")
398 .label("Check Main App")
399 .description("Check main Tauri application logic")
400 .arg("check")
401 .arg("--package")
402 .arg("src-tauri")
403 .category(CommandCategory::Lint)
404 .priority(85)
405 .tag("main")
406 .tag("check")
407 .tag("tauri")
408 .estimated_duration(8)
409 .build(),
410 );
411 }
412
413 if file_context.path.to_string_lossy().contains("command") {
414 commands.push(
415 CommandBuilder::new("tauri-test-commands", "cargo")
416 .label("Test All Commands")
417 .description("Test all Tauri command handlers")
418 .arg("test")
419 .arg("commands")
420 .category(CommandCategory::Test)
421 .priority(80)
422 .tag("commands")
423 .tag("test")
424 .tag("tauri")
425 .estimated_duration(20)
426 .build(),
427 );
428 }
429 }
430
431 commands
432 }
433
434 fn distribution_commands(&self, context: &ProjectContext) -> Vec<Command> {
436 let mut commands = vec![
437 CommandBuilder::new("tauri-bundle", "cargo")
438 .label("Create App Bundle")
439 .description("Create platform-specific app bundle")
440 .arg("tauri")
441 .arg("build")
442 .arg("--bundles")
443 .arg("all")
444 .category(CommandCategory::Deploy)
445 .priority(75)
446 .tag("bundle")
447 .tag("deploy")
448 .tag("tauri")
449 .estimated_duration(90)
450 .build(),
451 CommandBuilder::new("tauri-icon", "cargo")
452 .label("Generate App Icons")
453 .description("Generate app icons from source image")
454 .arg("tauri")
455 .arg("icon")
456 .category(CommandCategory::Generate)
457 .priority(65)
458 .tag("icon")
459 .tag("generate")
460 .tag("tauri")
461 .estimated_duration(5)
462 .build(),
463 CommandBuilder::new("tauri-add", "cargo")
464 .label("Add Tauri Plugin")
465 .description("Add a Tauri plugin to the project")
466 .arg("tauri")
467 .arg("add")
468 .category(CommandCategory::Generate)
469 .priority(60)
470 .tag("plugin")
471 .tag("add")
472 .tag("tauri")
473 .estimated_duration(10)
474 .build(),
475 CommandBuilder::new("tauri-migrate", "cargo")
476 .label("Migrate to Tauri v2")
477 .description("Migrate project from Tauri v1 to v2")
478 .arg("tauri")
479 .arg("migrate")
480 .category(CommandCategory::Generate)
481 .priority(55)
482 .tag("migrate")
483 .tag("upgrade")
484 .tag("tauri")
485 .estimated_duration(20)
486 .build(),
487 ];
488
489 let has_mobile = context.dependencies.iter().any(|d| {
491 d.features
492 .iter()
493 .any(|f| f.contains("mobile") || f.contains("android") || f.contains("ios"))
494 });
495
496 if has_mobile {
497 commands.extend(vec![
498 CommandBuilder::new("tauri-android-dev", "cargo")
499 .label("Android Dev")
500 .description("Run Tauri app on Android device/emulator")
501 .arg("tauri")
502 .arg("android")
503 .arg("dev")
504 .category(CommandCategory::Run)
505 .priority(70)
506 .tag("mobile")
507 .tag("android")
508 .tag("dev")
509 .tag("tauri")
510 .estimated_duration(60)
511 .build(),
512 CommandBuilder::new("tauri-ios-dev", "cargo")
513 .label("iOS Dev")
514 .description("Run Tauri app on iOS device/simulator")
515 .arg("tauri")
516 .arg("ios")
517 .arg("dev")
518 .category(CommandCategory::Run)
519 .priority(70)
520 .tag("mobile")
521 .tag("ios")
522 .tag("dev")
523 .tag("tauri")
524 .estimated_duration(60)
525 .build(),
526 ]);
527 }
528
529 if context
531 .dependencies
532 .iter()
533 .any(|d| d.features.iter().any(|f| f.contains("updater")))
534 {
535 commands.extend(vec![
536 CommandBuilder::new("tauri-sign", "cargo")
537 .label("Sign Application")
538 .description("Sign the application for distribution")
539 .arg("tauri")
540 .arg("signer")
541 .arg("sign")
542 .category(CommandCategory::Deploy)
543 .priority(70)
544 .tag("sign")
545 .tag("security")
546 .tag("deploy")
547 .tag("tauri")
548 .estimated_duration(15)
549 .build(),
550 CommandBuilder::new("tauri-updater", "cargo")
551 .label("Generate Update")
552 .description("Generate update package for auto-updater")
553 .arg("tauri")
554 .arg("build")
555 .arg("--config")
556 .arg("updater.active=true")
557 .category(CommandCategory::Deploy)
558 .priority(70)
559 .tag("updater")
560 .tag("deploy")
561 .tag("tauri")
562 .estimated_duration(75)
563 .build(),
564 ]);
565 }
566
567 commands
568 }
569}
570
571impl Default for TauriProvider {
572 fn default() -> Self {
573 Self::new()
574 }
575}
576
577#[cfg(test)]
578mod tests {
579 use super::*;
580 use crate::{BuildTarget, Dependency, ProjectType, TargetType, WorkspaceMember};
581 use std::collections::HashMap;
582 use std::path::PathBuf;
583
584 fn create_tauri_context() -> ProjectContext {
585 ProjectContext {
586 workspace_root: PathBuf::from("/test"),
587 current_file: None,
588 cursor_position: None,
589 project_type: ProjectType::Tauri,
590 dependencies: vec![Dependency {
591 name: "tauri".to_string(),
592 version: "1.5".to_string(),
593 features: vec!["api-all".to_string()],
594 optional: false,
595 dev_dependency: false,
596 }],
597 workspace_members: vec![WorkspaceMember {
598 name: "tauri-app".to_string(),
599 path: PathBuf::from("/test"),
600 package_type: ProjectType::Tauri,
601 }],
602 build_targets: vec![BuildTarget {
603 name: "main".to_string(),
604 target_type: TargetType::Binary,
605 path: PathBuf::from("/test/src-tauri/src/main.rs"),
606 }],
607 active_features: vec!["api-all".to_string()],
608 env_vars: HashMap::new(),
609 }
610 }
611
612 #[tokio::test]
613 async fn test_tauri_provider_can_handle() {
614 let provider = TauriProvider::new();
615 let context = create_tauri_context();
616
617 assert!(provider.can_handle(&context));
618 assert_eq!(provider.name(), "tauri");
619 assert_eq!(provider.priority(), 88);
620 }
621
622 #[tokio::test]
623 async fn test_tauri_commands_generation() {
624 let provider = TauriProvider::new();
625 let context = create_tauri_context();
626
627 let commands = provider.commands(&context).await.unwrap();
628
629 assert!(!commands.is_empty());
630
631 assert!(commands.iter().any(|c| c.id == "tauri-dev"));
633 assert!(commands.iter().any(|c| c.id == "tauri-dev-debug"));
634
635 assert!(commands.iter().any(|c| c.id == "tauri-build"));
637 assert!(commands.iter().any(|c| c.id == "tauri-build-debug"));
638
639 assert!(commands.iter().any(|c| c.id == "tauri-test"));
641 assert!(commands.iter().any(|c| c.id == "tauri-test-core"));
642
643 assert!(commands.iter().any(|c| c.id == "tauri-bundle"));
645 assert!(commands.iter().any(|c| c.id == "tauri-icon"));
646 }
647
648 #[tokio::test]
649 async fn test_tauri_cross_platform_builds() {
650 let provider = TauriProvider::new();
651 let mut context = create_tauri_context();
652
653 context.dependencies[0].features.push("updater".to_string());
655
656 let commands = provider.commands(&context).await.unwrap();
657
658 assert!(commands.iter().any(|c| c.id == "tauri-build-windows"));
660 assert!(commands.iter().any(|c| c.id == "tauri-build-macos"));
661 assert!(commands.iter().any(|c| c.id == "tauri-build-linux"));
662 }
663
664 #[tokio::test]
665 async fn test_tauri_updater_commands() {
666 let provider = TauriProvider::new();
667 let mut context = create_tauri_context();
668
669 context.dependencies[0].features.push("updater".to_string());
671
672 let commands = provider.commands(&context).await.unwrap();
673
674 assert!(commands.iter().any(|c| c.id == "tauri-sign"));
676 assert!(commands.iter().any(|c| c.id == "tauri-updater"));
677 }
678
679 #[tokio::test]
680 async fn test_tauri_command_priorities() {
681 let provider = TauriProvider::new();
682 let context = create_tauri_context();
683
684 let commands = provider.commands(&context).await.unwrap();
685
686 let dev_cmd = commands.iter().find(|c| c.id == "tauri-dev").unwrap();
688 assert_eq!(dev_cmd.priority, 95);
689
690 assert!(
692 commands
693 .iter()
694 .all(|c| c.tags.contains(&"tauri".to_string()))
695 );
696 }
697
698 #[tokio::test]
699 async fn test_tauri_dependency_detection() {
700 let provider = TauriProvider::new();
701 let mut context = create_tauri_context();
702 context.project_type = ProjectType::Binary; assert!(provider.can_handle(&context));
706 }
707}