Skip to main content

syncable_cli/wizard/
registry_selection.rs

1//! Registry selection step for deployment wizard
2
3use crate::platform::api::types::RegistrySummary;
4use crate::wizard::render::{display_step_header, status_indicator, wizard_render_config};
5use colored::Colorize;
6use inquire::{InquireError, Select};
7
8/// Result of registry selection step
9#[derive(Debug, Clone)]
10pub enum RegistrySelectionResult {
11    /// User selected an existing registry
12    Selected(RegistrySummary),
13    /// User wants to provision a new registry
14    ProvisionNew,
15    /// User wants to go back
16    Back,
17    /// User cancelled the wizard
18    Cancelled,
19}
20
21/// Display registry selection for container image storage
22pub fn select_registry(registries: &[RegistrySummary]) -> RegistrySelectionResult {
23    display_step_header(
24        4,
25        "Select Registry",
26        "Choose where to store container images. You can use an existing registry or provision a new one.",
27    );
28
29    // Filter to ready registries
30    let ready_registries: Vec<&RegistrySummary> =
31        registries.iter().filter(|r| r.is_ready).collect();
32
33    // Build options
34    let mut options: Vec<String> = ready_registries
35        .iter()
36        .map(|r| {
37            format!(
38                "{} {}  {}",
39                status_indicator(r.is_ready),
40                r.name.cyan(),
41                r.region.dimmed()
42            )
43        })
44        .collect();
45
46    // Always offer to provision new
47    options.push(format!("{} Provision new registry", "+".green()));
48
49    // Add back option
50    options.push("← Back".dimmed().to_string());
51
52    let selection = Select::new("Select registry:", options.clone())
53        .with_render_config(wizard_render_config())
54        .with_help_message("↑↓ to move, Enter to select, Esc to cancel")
55        .with_page_size(6)
56        .prompt();
57
58    match selection {
59        Ok(answer) => {
60            if answer.contains("Back") {
61                return RegistrySelectionResult::Back;
62            }
63
64            if answer.contains("Provision new") {
65                println!(
66                    "\n{} Will provision new registry during deployment",
67                    "→".cyan()
68                );
69                return RegistrySelectionResult::ProvisionNew;
70            }
71
72            // Find selected registry by name
73            let selected = ready_registries
74                .iter()
75                .find(|r| answer.contains(&r.name))
76                .copied();
77
78            match selected {
79                Some(registry) => {
80                    println!(
81                        "\n{} Selected registry: {} ({})",
82                        "✓".green(),
83                        registry.name,
84                        registry.region
85                    );
86                    RegistrySelectionResult::Selected(registry.clone())
87                }
88                None => RegistrySelectionResult::Cancelled,
89            }
90        }
91        Err(InquireError::OperationCanceled) | Err(InquireError::OperationInterrupted) => {
92            println!("\n{}", "Wizard cancelled.".dimmed());
93            RegistrySelectionResult::Cancelled
94        }
95        Err(_) => RegistrySelectionResult::Cancelled,
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_registry_selection_result_variants() {
105        let registry = RegistrySummary {
106            id: "r1".to_string(),
107            name: "main".to_string(),
108            region: "us-central1".to_string(),
109            is_ready: true,
110        };
111        let _ = RegistrySelectionResult::Selected(registry);
112        let _ = RegistrySelectionResult::ProvisionNew;
113        let _ = RegistrySelectionResult::Back;
114        let _ = RegistrySelectionResult::Cancelled;
115    }
116}