syncable_cli/wizard/
target_selection.rs1use crate::platform::api::types::{DeploymentTarget, ProviderDeploymentStatus};
4use crate::wizard::render::{display_step_header, wizard_render_config};
5use colored::Colorize;
6use inquire::{InquireError, Select};
7
8#[derive(Debug, Clone)]
10pub enum TargetSelectionResult {
11 Selected(DeploymentTarget),
13 Back,
15 Cancelled,
17}
18
19pub fn select_target(provider_status: &ProviderDeploymentStatus) -> TargetSelectionResult {
21 display_step_header(
22 2,
23 "Select Target",
24 "Choose how to deploy your service. Cloud Runner is fully managed. Kubernetes gives you more control.",
25 );
26
27 let available_targets = provider_status.available_targets();
28
29 if available_targets.is_empty() {
30 println!(
31 "\n{}",
32 "No deployment targets available for this provider.".red()
33 );
34 return TargetSelectionResult::Cancelled;
35 }
36
37 let mut options: Vec<String> = available_targets
39 .iter()
40 .map(|t| match t {
41 DeploymentTarget::CloudRunner => {
42 format!(
43 "{} {}",
44 "Cloud Runner".cyan(),
45 "Fully managed, auto-scaling containers".dimmed()
46 )
47 }
48 DeploymentTarget::Kubernetes => {
49 let cluster_count = provider_status
50 .clusters
51 .iter()
52 .filter(|c| c.is_healthy)
53 .count();
54 format!(
55 "{} {} cluster{} available",
56 "Kubernetes".cyan(),
57 cluster_count,
58 if cluster_count == 1 { "" } else { "s" }
59 )
60 }
61 })
62 .collect();
63
64 options.push("← Back to provider selection".dimmed().to_string());
66
67 let selection = Select::new("Select deployment target:", options.clone())
68 .with_render_config(wizard_render_config())
69 .with_help_message("↑↓ to move, Enter to select, Esc to cancel")
70 .with_page_size(4)
71 .prompt();
72
73 match selection {
74 Ok(answer) => {
75 if answer.contains("Back") {
76 return TargetSelectionResult::Back;
77 }
78
79 let target = if answer.contains("Cloud Runner") {
80 DeploymentTarget::CloudRunner
81 } else {
82 DeploymentTarget::Kubernetes
83 };
84
85 println!("\n{} Selected: {}", "✓".green(), target.display_name());
86 TargetSelectionResult::Selected(target)
87 }
88 Err(InquireError::OperationCanceled) | Err(InquireError::OperationInterrupted) => {
89 println!("\n{}", "Wizard cancelled.".dimmed());
90 TargetSelectionResult::Cancelled
91 }
92 Err(_) => TargetSelectionResult::Cancelled,
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_target_selection_result_variants() {
102 let _ = TargetSelectionResult::Selected(DeploymentTarget::CloudRunner);
103 let _ = TargetSelectionResult::Selected(DeploymentTarget::Kubernetes);
104 let _ = TargetSelectionResult::Back;
105 let _ = TargetSelectionResult::Cancelled;
106 }
107}