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> = registries.iter().filter(|r| r.is_ready).collect();
31
32    // Build options
33    let mut options: Vec<String> = ready_registries
34        .iter()
35        .map(|r| {
36            format!(
37                "{} {}  {}",
38                status_indicator(r.is_ready),
39                r.name.cyan(),
40                r.region.dimmed()
41            )
42        })
43        .collect();
44
45    // Always offer to provision new
46    options.push(format!("{} Provision new registry", "+".green()));
47
48    // Add back option
49    options.push("← Back".dimmed().to_string());
50
51    let selection = Select::new("Select registry:", options.clone())
52        .with_render_config(wizard_render_config())
53        .with_help_message("↑↓ to move, Enter to select, Esc to cancel")
54        .with_page_size(6)
55        .prompt();
56
57    match selection {
58        Ok(answer) => {
59            if answer.contains("Back") {
60                return RegistrySelectionResult::Back;
61            }
62
63            if answer.contains("Provision new") {
64                println!("\n{} Will provision new registry during deployment", "→".cyan());
65                return RegistrySelectionResult::ProvisionNew;
66            }
67
68            // Find selected registry by name
69            let selected = ready_registries
70                .iter()
71                .find(|r| answer.contains(&r.name))
72                .copied();
73
74            match selected {
75                Some(registry) => {
76                    println!(
77                        "\n{} Selected registry: {} ({})",
78                        "✓".green(),
79                        registry.name,
80                        registry.region
81                    );
82                    RegistrySelectionResult::Selected(registry.clone())
83                }
84                None => RegistrySelectionResult::Cancelled,
85            }
86        }
87        Err(InquireError::OperationCanceled) | Err(InquireError::OperationInterrupted) => {
88            println!("\n{}", "Wizard cancelled.".dimmed());
89            RegistrySelectionResult::Cancelled
90        }
91        Err(_) => RegistrySelectionResult::Cancelled,
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn test_registry_selection_result_variants() {
101        let registry = RegistrySummary {
102            id: "r1".to_string(),
103            name: "main".to_string(),
104            region: "us-central1".to_string(),
105            is_ready: true,
106        };
107        let _ = RegistrySelectionResult::Selected(registry);
108        let _ = RegistrySelectionResult::ProvisionNew;
109        let _ = RegistrySelectionResult::Back;
110        let _ = RegistrySelectionResult::Cancelled;
111    }
112}