use anyhow::{anyhow, Result};
use colored::Colorize;
use inquire::{Confirm, MultiSelect, Select};
use crate::core::sync::{get_databases, parse_environment, perform_sync, SyncConfig, SyncOptions};
pub struct SyncParams {
pub from: Option<String>,
pub to: Option<String>,
pub db: Option<String>,
pub target_db: Option<String>,
pub backup: Option<bool>,
pub drop: Option<bool>,
pub clear: Option<bool>,
pub interactive: bool,
}
#[deprecated(since = "0.1.0", note = "use execute_with_params instead")]
#[allow(dead_code, clippy::too_many_arguments)]
pub async fn execute(
from: Option<String>,
to: Option<String>,
db: Option<String>,
target_db: Option<String>,
backup: Option<bool>,
drop: Option<bool>,
clear: Option<bool>,
interactive: bool,
) -> Result<()> {
let params = SyncParams {
from,
to,
db,
target_db,
backup,
drop,
clear,
interactive,
};
execute_with_params(params).await
}
pub async fn execute_with_params(params: SyncParams) -> Result<()> {
if params.interactive {
execute_interactive(¶ms).await
} else {
execute_non_interactive(¶ms).await
}
}
async fn execute_interactive(params: &SyncParams) -> Result<()> {
let source_env = if let Some(from_str) = ¶ms.from {
parse_environment(from_str)?
} else {
let env_options = crate::config::get_available_environments();
if env_options.is_empty() {
return Err(anyhow!("No MongoDB environments configured. Use 'info' command to see how to configure environments."));
}
Select::new("1. Select source environment:", env_options).prompt()?
};
let source_dbs = get_databases(&source_env).await?;
if source_dbs.is_empty() {
return Err(anyhow!("No databases found in source environment"));
}
let source_db = if let Some(db_str) = params.db.clone() {
if !source_dbs.contains(&db_str) {
return Err(anyhow!(
"Database '{}' not found in source environment",
db_str
));
}
db_str
} else {
Select::new("2. Select source database:", source_dbs)
.with_page_size(10) .with_help_message("Type to filter databases")
.prompt()?
};
let target_env = if let Some(to_str) = ¶ms.to {
parse_environment(to_str)?
} else {
let env_options = crate::config::get_available_environments();
if env_options.is_empty() {
return Err(anyhow!("No MongoDB environments configured. Use 'info' command to see how to configure environments."));
}
Select::new("3. Select target environment:", env_options).prompt()?
};
let target_db_name = if let Some(tgt_db) = ¶ms.target_db {
tgt_db.clone()
} else {
let target_dbs = get_databases(&target_env).await?;
if target_dbs.is_empty() {
return Err(anyhow!("No databases found in target environment"));
}
let default_index = target_dbs.iter().position(|db| *db == source_db);
let select = Select::new("4. Select target database:", target_dbs)
.with_page_size(10) .with_help_message("Type to filter databases");
let select = if let Some(idx) = default_index {
select.with_starting_cursor(idx)
} else {
select
};
select.prompt()?
};
let mut options = SyncOptions {
create_backup: params.backup.unwrap_or(true),
drop_collections: params.drop.unwrap_or(true),
clear_collections: params.clear.unwrap_or(false),
};
let option_labels = vec![
"Create backup before import",
"Drop collections during import",
"Clear collections during import (ignored if drop is enabled)",
];
let mut defaults = Vec::new();
if options.create_backup {
defaults.push(0);
}
if options.drop_collections {
defaults.push(1);
}
if options.clear_collections {
defaults.push(2);
}
let selected_options = MultiSelect::new("5. Configure sync settings:", option_labels)
.with_default(&defaults)
.with_help_message("Space to toggle, Enter to confirm")
.prompt()?;
options.create_backup = selected_options.contains(&"Create backup before import");
options.drop_collections = selected_options.contains(&"Drop collections during import");
options.clear_collections =
selected_options.contains(&"Clear collections during import (ignored if drop is enabled)");
options.update_collection_settings();
let operation_pattern = format!(
"{}:{} → {}:{} B:[{}] D:[{}] C:[{}]",
source_env,
source_db,
target_env,
target_db_name,
if options.create_backup {
"✓".green()
} else {
"✗".yellow()
},
if options.drop_collections {
"✓".green()
} else {
"✗".yellow()
},
if options.clear_collections {
"✓".green()
} else {
"✗".yellow()
}
);
let proceed = Confirm::new("6. Ready to proceed with synchronization?")
.with_default(true)
.with_help_message(&operation_pattern)
.prompt()?;
if !proceed {
return Ok(());
}
let config = SyncConfig {
source_env,
target_env,
source_db,
target_db: target_db_name,
options,
};
perform_sync(config).await
}
async fn execute_non_interactive(params: &SyncParams) -> Result<()> {
let source_env = match ¶ms.from {
Some(env_str) => parse_environment(env_str)?,
None => return Err(anyhow!("Source environment is required (--from)")),
};
let target_env = match ¶ms.to {
Some(env_str) => parse_environment(env_str)?,
None => return Err(anyhow!("Target environment is required (--to)")),
};
let source_db = match ¶ms.db {
Some(db_str) => db_str.clone(),
None => return Err(anyhow!("Source database is required (--db)")),
};
let target_db_name = params
.target_db
.clone()
.unwrap_or_else(|| source_db.clone());
let mut options = SyncOptions {
create_backup: params.backup.unwrap_or(true),
drop_collections: params.drop.unwrap_or(true),
clear_collections: params.clear.unwrap_or(false),
};
options.update_collection_settings();
let config = SyncConfig {
source_env,
target_env,
source_db,
target_db: target_db_name,
options,
};
perform_sync(config).await
}