syncable_cli/wizard/
environment_selection.rs1use crate::platform::api::types::Environment;
6use crate::platform::api::PlatformApiClient;
7use crate::wizard::render::{display_step_header, wizard_render_config};
8use colored::Colorize;
9use inquire::{InquireError, Select};
10use std::fmt;
11
12#[derive(Debug, Clone)]
14pub enum EnvironmentSelectionResult {
15 Selected(Environment),
17 CreateNew,
19 Cancelled,
21 Error(String),
23}
24
25struct EnvironmentOption {
27 environment: Environment,
28}
29
30impl fmt::Display for EnvironmentOption {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 write!(
33 f,
34 "{} {}",
35 self.environment.name.cyan(),
36 self.environment.environment_type.to_string().dimmed()
37 )
38 }
39}
40
41struct CreateNewOption;
43
44impl fmt::Display for CreateNewOption {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 write!(f, "{}", "+ Create new environment".bright_green())
47 }
48}
49
50enum SelectionItem {
52 Environment(EnvironmentOption),
53 CreateNew(CreateNewOption),
54}
55
56impl fmt::Display for SelectionItem {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 match self {
59 SelectionItem::Environment(env) => env.fmt(f),
60 SelectionItem::CreateNew(create) => create.fmt(f),
61 }
62 }
63}
64
65pub async fn select_environment(
67 client: &PlatformApiClient,
68 project_id: &str,
69) -> EnvironmentSelectionResult {
70 display_step_header(
71 0,
72 "Select Environment",
73 "Choose the environment to deploy to.",
74 );
75
76 let environments = match client.list_environments(project_id).await {
78 Ok(envs) => envs,
79 Err(e) => {
80 return EnvironmentSelectionResult::Error(format!(
81 "Failed to fetch environments: {}",
82 e
83 ));
84 }
85 };
86
87 if environments.is_empty() {
88 println!(
89 "\n{} No environments found. Let's create one first.",
90 "ℹ".cyan()
91 );
92 return EnvironmentSelectionResult::CreateNew;
93 }
94
95 let mut options: Vec<SelectionItem> = environments
97 .into_iter()
98 .map(|env| SelectionItem::Environment(EnvironmentOption { environment: env }))
99 .collect();
100
101 options.push(SelectionItem::CreateNew(CreateNewOption));
103
104 let selection = Select::new("Select environment:", options)
105 .with_render_config(wizard_render_config())
106 .with_help_message("Use ↑/↓ to navigate, Enter to select")
107 .prompt();
108
109 match selection {
110 Ok(SelectionItem::Environment(env_opt)) => {
111 println!(
112 "\n{} Selected environment: {}",
113 "✓".green(),
114 env_opt.environment.name.cyan()
115 );
116 EnvironmentSelectionResult::Selected(env_opt.environment)
117 }
118 Ok(SelectionItem::CreateNew(_)) => EnvironmentSelectionResult::CreateNew,
119 Err(InquireError::OperationCanceled) | Err(InquireError::OperationInterrupted) => {
120 EnvironmentSelectionResult::Cancelled
121 }
122 Err(_) => EnvironmentSelectionResult::Cancelled,
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_environment_selection_result_variants() {
132 let env = Environment {
133 id: "test-id".to_string(),
134 name: "prod".to_string(),
135 project_id: "proj-1".to_string(),
136 environment_type: "cloud".to_string(),
137 cluster_id: None,
138 namespace: None,
139 description: None,
140 is_active: true,
141 created_at: None,
142 updated_at: None,
143 };
144 let _ = EnvironmentSelectionResult::Selected(env);
145 let _ = EnvironmentSelectionResult::CreateNew;
146 let _ = EnvironmentSelectionResult::Cancelled;
147 let _ = EnvironmentSelectionResult::Error("test".to_string());
148 }
149}