syncable_cli/wizard/
environment_selection.rs

1//! Environment selection step for the deployment wizard
2//!
3//! Prompts user to select an environment or create a new one.
4
5use 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/// Result of environment selection step
13#[derive(Debug, Clone)]
14pub enum EnvironmentSelectionResult {
15    /// User selected an environment
16    Selected(Environment),
17    /// User wants to create a new environment
18    CreateNew,
19    /// User cancelled the wizard
20    Cancelled,
21    /// An error occurred
22    Error(String),
23}
24
25/// Wrapper for displaying environment options in the selection menu
26struct 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
41/// Option to create a new environment
42struct 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
50/// Selection menu item that can be either an environment or create new
51enum 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
65/// Prompt user to select an environment for deployment
66pub 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    // Fetch environments
77    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    // Build selection options
96    let mut options: Vec<SelectionItem> = environments
97        .into_iter()
98        .map(|env| SelectionItem::Environment(EnvironmentOption { environment: env }))
99        .collect();
100
101    // Add create new option at the end
102    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}