syncable_cli/wizard/
cluster_selection.rs1use crate::platform::api::types::ClusterSummary;
4use crate::wizard::render::{display_step_header, status_indicator, wizard_render_config};
5use colored::Colorize;
6use inquire::{InquireError, Select};
7
8#[derive(Debug, Clone)]
10pub enum ClusterSelectionResult {
11 Selected(ClusterSummary),
13 Back,
15 Cancelled,
17}
18
19pub fn select_cluster(clusters: &[ClusterSummary]) -> ClusterSelectionResult {
21 display_step_header(
22 3,
23 "Select Cluster",
24 "Choose which Kubernetes cluster to deploy to.",
25 );
26
27 let healthy_clusters: Vec<&ClusterSummary> = clusters.iter().filter(|c| c.is_healthy).collect();
29
30 if healthy_clusters.is_empty() {
31 println!(
32 "\n{}",
33 "No healthy clusters available. Provision a cluster in platform settings.".red()
34 );
35 return ClusterSelectionResult::Cancelled;
36 }
37
38 let mut options: Vec<String> = healthy_clusters
40 .iter()
41 .map(|c| {
42 format!(
43 "{} {} {}",
44 status_indicator(c.is_healthy),
45 c.name.cyan(),
46 c.region.dimmed()
47 )
48 })
49 .collect();
50
51 options.push("← Back to target selection".dimmed().to_string());
53
54 let selection = Select::new("Select cluster:", options.clone())
55 .with_render_config(wizard_render_config())
56 .with_help_message("↑↓ to move, Enter to select, Esc to cancel")
57 .with_page_size(6)
58 .prompt();
59
60 match selection {
61 Ok(answer) => {
62 if answer.contains("Back") {
63 return ClusterSelectionResult::Back;
64 }
65
66 let selected = healthy_clusters
68 .iter()
69 .find(|c| answer.contains(&c.name))
70 .copied();
71
72 match selected {
73 Some(cluster) => {
74 println!(
75 "\n{} Selected cluster: {} ({})",
76 "✓".green(),
77 cluster.name,
78 cluster.region
79 );
80 ClusterSelectionResult::Selected(cluster.clone())
81 }
82 None => ClusterSelectionResult::Cancelled,
83 }
84 }
85 Err(InquireError::OperationCanceled) | Err(InquireError::OperationInterrupted) => {
86 println!("\n{}", "Wizard cancelled.".dimmed());
87 ClusterSelectionResult::Cancelled
88 }
89 Err(_) => ClusterSelectionResult::Cancelled,
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn test_cluster_selection_result_variants() {
99 let cluster = ClusterSummary {
100 id: "c1".to_string(),
101 name: "prod".to_string(),
102 region: "us-central1".to_string(),
103 is_healthy: true,
104 };
105 let _ = ClusterSelectionResult::Selected(cluster);
106 let _ = ClusterSelectionResult::Back;
107 let _ = ClusterSelectionResult::Cancelled;
108 }
109}