use crate::errors::Error;
use crate::output::{MergeResponse, print_json};
use crate::sqlite::Database;
use std::path::Path;
use std::process::ExitCode;
use std::time::Duration;
fn wrap_busy<T>(result: Result<T, Error>) -> Result<T, Error> {
match result {
Ok(v) => Ok(v),
Err(Error::SqliteModule(msg)) if msg.contains("database is locked") => {
Err(Error::Config(
"Database is locked. Another process (likely the MCP server) is holding a lock. Stop the MCP server and retry.".to_string()
))
}
Err(e) => Err(e),
}
}
pub(crate) fn build_mcp_restart_notice() -> String {
"Note: If a vipune MCP server is running, it holds its project_id from startup. Restart the MCP server to see rows under the new project id.".to_string()
}
pub fn handle_merge(
db_path: &Path,
from_project_id: &str,
to_project_id: &str,
json: bool,
) -> Result<ExitCode, Error> {
let mut db = Database::open(db_path).map_err(|e| {
let err_msg = e.to_string();
if err_msg.contains("database is locked") {
return Error::Config(
"Database is locked. Another process (likely the MCP server) is holding a lock. Stop the MCP server and retry.".to_string()
);
}
Error::Config(err_msg)
})?;
wrap_busy(db.set_busy_timeout(Duration::ZERO).map_err(Error::from))?;
let rows_moved = wrap_busy(
db.merge_project_ids(from_project_id, to_project_id)
.map_err(Error::from),
)?;
let response = MergeResponse {
from: from_project_id.to_string(),
to: to_project_id.to_string(),
rows_moved,
};
if json {
print_json(&response);
} else {
println!(
"Merged {} row(s) from '{}' to '{}'",
response.rows_moved, response.from, response.to
);
}
if response.rows_moved > 0 {
eprintln!("\n{}", build_mcp_restart_notice());
}
Ok(ExitCode::SUCCESS)
}