syncable_cli/wizard/
cloud_provider_data.rs

1//! Cloud provider regions and machine types for the deployment wizard
2//!
3//! This module contains static data for cloud provider options,
4//! matching the frontend's cloudProviderData.ts for consistency.
5
6use crate::platform::api::types::CloudProvider;
7
8/// A cloud region/location option
9#[derive(Debug, Clone)]
10pub struct CloudRegion {
11    /// Region ID (e.g., "nbg1", "us-central1")
12    pub id: &'static str,
13    /// Human-readable name (e.g., "Nuremberg", "Iowa")
14    pub name: &'static str,
15    /// Geographic location (e.g., "Germany", "US Central")
16    pub location: &'static str,
17}
18
19/// A machine/instance type option
20#[derive(Debug, Clone)]
21pub struct MachineType {
22    /// Machine type ID (e.g., "cx22", "e2-small")
23    pub id: &'static str,
24    /// Display name
25    pub name: &'static str,
26    /// Number of vCPUs (as string to handle fractional)
27    pub cpu: &'static str,
28    /// Memory amount (e.g., "4 GB")
29    pub memory: &'static str,
30    /// Optional description (e.g., "Shared Intel", "ARM64")
31    pub description: Option<&'static str>,
32}
33
34// =============================================================================
35// Hetzner Cloud
36// =============================================================================
37
38/// Hetzner Cloud locations
39pub static HETZNER_LOCATIONS: &[CloudRegion] = &[
40    // Europe
41    CloudRegion { id: "nbg1", name: "Nuremberg", location: "Germany" },
42    CloudRegion { id: "fsn1", name: "Falkenstein", location: "Germany" },
43    CloudRegion { id: "hel1", name: "Helsinki", location: "Finland" },
44    // Americas
45    CloudRegion { id: "ash", name: "Ashburn", location: "US East" },
46    CloudRegion { id: "hil", name: "Hillsboro", location: "US West" },
47    // Asia Pacific
48    CloudRegion { id: "sin", name: "Singapore", location: "Southeast Asia" },
49];
50
51/// Hetzner Cloud server types (updated January 2026 naming)
52pub static HETZNER_SERVER_TYPES: &[MachineType] = &[
53    // Shared vCPU - CX Series (Intel/AMD cost-optimized)
54    MachineType { id: "cx23", name: "CX23", cpu: "2", memory: "4 GB", description: Some("Shared Intel/AMD") },
55    MachineType { id: "cx33", name: "CX33", cpu: "4", memory: "8 GB", description: Some("Shared Intel/AMD") },
56    MachineType { id: "cx43", name: "CX43", cpu: "8", memory: "16 GB", description: Some("Shared Intel/AMD") },
57    MachineType { id: "cx53", name: "CX53", cpu: "16", memory: "32 GB", description: Some("Shared Intel/AMD") },
58    // Shared vCPU - CPX Series (AMD regular)
59    MachineType { id: "cpx22", name: "CPX22", cpu: "2", memory: "4 GB", description: Some("Shared AMD") },
60    MachineType { id: "cpx32", name: "CPX32", cpu: "4", memory: "8 GB", description: Some("Shared AMD") },
61    MachineType { id: "cpx42", name: "CPX42", cpu: "8", memory: "16 GB", description: Some("Shared AMD") },
62    MachineType { id: "cpx52", name: "CPX52", cpu: "12", memory: "24 GB", description: Some("Shared AMD") },
63    MachineType { id: "cpx62", name: "CPX62", cpu: "16", memory: "32 GB", description: Some("Shared AMD") },
64    // Dedicated vCPU - CCX Series (AMD)
65    MachineType { id: "ccx13", name: "CCX13", cpu: "2", memory: "8 GB", description: Some("Dedicated AMD") },
66    MachineType { id: "ccx23", name: "CCX23", cpu: "4", memory: "16 GB", description: Some("Dedicated AMD") },
67    MachineType { id: "ccx33", name: "CCX33", cpu: "8", memory: "32 GB", description: Some("Dedicated AMD") },
68    MachineType { id: "ccx43", name: "CCX43", cpu: "16", memory: "64 GB", description: Some("Dedicated AMD") },
69    MachineType { id: "ccx53", name: "CCX53", cpu: "32", memory: "128 GB", description: Some("Dedicated AMD") },
70    MachineType { id: "ccx63", name: "CCX63", cpu: "48", memory: "192 GB", description: Some("Dedicated AMD") },
71    // ARM - CAX Series (Ampere)
72    MachineType { id: "cax11", name: "CAX11", cpu: "2", memory: "4 GB", description: Some("ARM64 Ampere") },
73    MachineType { id: "cax21", name: "CAX21", cpu: "4", memory: "8 GB", description: Some("ARM64 Ampere") },
74    MachineType { id: "cax31", name: "CAX31", cpu: "8", memory: "16 GB", description: Some("ARM64 Ampere") },
75    MachineType { id: "cax41", name: "CAX41", cpu: "16", memory: "32 GB", description: Some("ARM64 Ampere") },
76];
77
78// =============================================================================
79// GCP (Google Cloud Platform)
80// =============================================================================
81
82/// GCP regions
83pub static GCP_REGIONS: &[CloudRegion] = &[
84    // Americas
85    CloudRegion { id: "us-central1", name: "Iowa", location: "US Central" },
86    CloudRegion { id: "us-east1", name: "South Carolina", location: "US East" },
87    CloudRegion { id: "us-east4", name: "Virginia", location: "US East" },
88    CloudRegion { id: "us-west1", name: "Oregon", location: "US West" },
89    CloudRegion { id: "us-west2", name: "Los Angeles", location: "US West" },
90    // Europe
91    CloudRegion { id: "europe-west1", name: "Belgium", location: "Europe" },
92    CloudRegion { id: "europe-west2", name: "London", location: "UK" },
93    CloudRegion { id: "europe-west3", name: "Frankfurt", location: "Germany" },
94    CloudRegion { id: "europe-west4", name: "Netherlands", location: "Europe" },
95    CloudRegion { id: "europe-north1", name: "Finland", location: "Europe" },
96    // Asia Pacific
97    CloudRegion { id: "asia-east1", name: "Taiwan", location: "Asia Pacific" },
98    CloudRegion { id: "asia-northeast1", name: "Tokyo", location: "Japan" },
99    CloudRegion { id: "asia-southeast1", name: "Singapore", location: "Southeast Asia" },
100    CloudRegion { id: "australia-southeast1", name: "Sydney", location: "Australia" },
101];
102
103/// GCP machine types (Compute Engine)
104pub static GCP_MACHINE_TYPES: &[MachineType] = &[
105    // E2 Series (Cost-optimized)
106    MachineType { id: "e2-micro", name: "e2-micro", cpu: "0.25", memory: "1 GB", description: Some("Shared-core") },
107    MachineType { id: "e2-small", name: "e2-small", cpu: "0.5", memory: "2 GB", description: Some("Shared-core") },
108    MachineType { id: "e2-medium", name: "e2-medium", cpu: "1", memory: "4 GB", description: Some("Shared-core") },
109    MachineType { id: "e2-standard-2", name: "e2-standard-2", cpu: "2", memory: "8 GB", description: None },
110    MachineType { id: "e2-standard-4", name: "e2-standard-4", cpu: "4", memory: "16 GB", description: None },
111    MachineType { id: "e2-standard-8", name: "e2-standard-8", cpu: "8", memory: "32 GB", description: None },
112    // N2 Series (Balanced)
113    MachineType { id: "n2-standard-2", name: "n2-standard-2", cpu: "2", memory: "8 GB", description: None },
114    MachineType { id: "n2-standard-4", name: "n2-standard-4", cpu: "4", memory: "16 GB", description: None },
115    MachineType { id: "n2-standard-8", name: "n2-standard-8", cpu: "8", memory: "32 GB", description: None },
116];
117
118// =============================================================================
119// Helper Functions
120// =============================================================================
121
122/// Get regions for a cloud provider
123pub fn get_regions_for_provider(provider: &CloudProvider) -> &'static [CloudRegion] {
124    match provider {
125        CloudProvider::Hetzner => HETZNER_LOCATIONS,
126        CloudProvider::Gcp => GCP_REGIONS,
127        _ => &[], // AWS, Azure not yet supported for Cloud Runner
128    }
129}
130
131/// Get machine types for a cloud provider
132pub fn get_machine_types_for_provider(provider: &CloudProvider) -> &'static [MachineType] {
133    match provider {
134        CloudProvider::Hetzner => HETZNER_SERVER_TYPES,
135        CloudProvider::Gcp => GCP_MACHINE_TYPES,
136        _ => &[], // AWS, Azure not yet supported for Cloud Runner
137    }
138}
139
140/// Get default region for a provider
141pub fn get_default_region(provider: &CloudProvider) -> &'static str {
142    match provider {
143        CloudProvider::Hetzner => "nbg1",
144        CloudProvider::Gcp => "us-central1",
145        _ => "",
146    }
147}
148
149/// Get default machine type for a provider
150pub fn get_default_machine_type(provider: &CloudProvider) -> &'static str {
151    match provider {
152        CloudProvider::Hetzner => "cx23",
153        CloudProvider::Gcp => "e2-small",
154        _ => "",
155    }
156}
157
158/// Format region for display: "Nuremberg (Germany)"
159pub fn format_region_display(region: &CloudRegion) -> String {
160    format!("{} ({})", region.name, region.location)
161}
162
163/// Format machine type for display: "cx22 · 2 vCPU · 4 GB"
164pub fn format_machine_type_display(machine: &MachineType) -> String {
165    let base = format!("{} · {} vCPU · {}", machine.name, machine.cpu, machine.memory);
166    if let Some(desc) = machine.description {
167        format!("{} · {}", base, desc)
168    } else {
169        base
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    #[test]
178    fn test_hetzner_locations() {
179        assert!(!HETZNER_LOCATIONS.is_empty());
180        assert!(HETZNER_LOCATIONS.iter().any(|r| r.id == "nbg1"));
181    }
182
183    #[test]
184    fn test_hetzner_machine_types() {
185        assert!(!HETZNER_SERVER_TYPES.is_empty());
186        assert!(HETZNER_SERVER_TYPES.iter().any(|m| m.id == "cx23"));
187    }
188
189    #[test]
190    fn test_gcp_regions() {
191        assert!(!GCP_REGIONS.is_empty());
192        assert!(GCP_REGIONS.iter().any(|r| r.id == "us-central1"));
193    }
194
195    #[test]
196    fn test_gcp_machine_types() {
197        assert!(!GCP_MACHINE_TYPES.is_empty());
198        assert!(GCP_MACHINE_TYPES.iter().any(|m| m.id == "e2-small"));
199    }
200
201    #[test]
202    fn test_get_regions_for_provider() {
203        let hetzner_regions = get_regions_for_provider(&CloudProvider::Hetzner);
204        assert!(!hetzner_regions.is_empty());
205
206        let gcp_regions = get_regions_for_provider(&CloudProvider::Gcp);
207        assert!(!gcp_regions.is_empty());
208    }
209
210    #[test]
211    fn test_format_region_display() {
212        let region = &HETZNER_LOCATIONS[0];
213        let display = format_region_display(region);
214        assert!(display.contains("Nuremberg"));
215        assert!(display.contains("Germany"));
216    }
217
218    #[test]
219    fn test_format_machine_type_display() {
220        let machine = &HETZNER_SERVER_TYPES[0];
221        let display = format_machine_type_display(machine);
222        assert!(display.contains("CX23"));
223        assert!(display.contains("2 vCPU"));
224        assert!(display.contains("4 GB"));
225    }
226}