database_replicator/serendb/
picker.rs1use crate::serendb::{Branch, ConsoleClient, Project};
5use anyhow::{Context, Result};
6use dialoguer::{theme::ColorfulTheme, Select};
7
8#[derive(Debug, Clone)]
10pub struct TargetSelection {
11 pub project: Project,
12 pub branch: Branch,
13 pub databases: Vec<String>,
14}
15
16pub async fn select_target(
19 client: &ConsoleClient,
20 source_databases: &[String],
21) -> Result<TargetSelection> {
22 println!("\n==================================================");
23 println!("SerenDB Target Selection");
24 println!("==================================================\n");
25
26 let projects = client.list_projects().await?;
27
28 if projects.is_empty() {
29 anyhow::bail!(
30 "No SerenDB projects found for this API key.\n\
31 Create a project at: https://console.serendb.com"
32 );
33 }
34
35 let project_labels: Vec<String> = projects
36 .iter()
37 .map(|p| {
38 let short_id: String = p.id.chars().take(8).collect();
39 format!("{} ({})", p.name, short_id)
40 })
41 .collect();
42
43 let project_idx = Select::with_theme(&ColorfulTheme::default())
44 .with_prompt("Select target project")
45 .items(&project_labels)
46 .default(0)
47 .interact()
48 .context("Project selection cancelled")?;
49
50 let project = projects[project_idx].clone();
51 println!(" Selected project: {}\n", project.name);
52
53 let branch = client.get_default_branch(&project.id).await?;
54 println!(" Using branch: {}\n", branch.name);
55
56 let existing = client.list_databases(&project.id, &branch.id).await?;
57 let existing_names: Vec<String> = existing.iter().map(|d| d.name.clone()).collect();
58
59 println!("Source databases to replicate: {:?}", source_databases);
60 println!("Existing target databases: {:?}\n", existing_names);
61
62 let mut target_databases = Vec::new();
63 for source_db in source_databases {
64 if existing_names.contains(source_db) {
65 println!(" \u{2713} {}", source_db);
66 } else {
67 println!(" + {} (will be created)", source_db);
68 }
69 target_databases.push(source_db.clone());
70 }
71
72 println!();
73
74 Ok(TargetSelection {
75 project,
76 branch,
77 databases: target_databases,
78 })
79}
80
81pub async fn create_missing_databases(
83 client: &ConsoleClient,
84 project_id: &str,
85 branch_id: &str,
86 databases: &[String],
87) -> Result<()> {
88 let existing = client.list_databases(project_id, branch_id).await?;
89 let existing_names: Vec<String> = existing.iter().map(|d| d.name.clone()).collect();
90
91 for db_name in databases {
92 if !existing_names.contains(db_name) {
93 println!(" Creating database '{}'...", db_name);
94 client
95 .create_database(project_id, branch_id, db_name)
96 .await?;
97 println!(" \u{2713} Created '{}'", db_name);
98 }
99 }
100
101 Ok(())
102}
103
104#[cfg(test)]
105mod tests {
106 }