use serde::Serialize;
#[cfg(feature = "postgres")]
use tokio_postgres::Client;
use crate::config::WaypointConfig;
use crate::db::DbClient;
use crate::dialect::DialectKind;
use crate::error::{Result, WaypointError};
use crate::schema::{self, SchemaDiff};
pub enum DiffTarget {
Database(String),
}
#[derive(Debug, Serialize)]
pub struct DiffReport {
pub diffs: Vec<SchemaDiff>,
pub generated_sql: String,
pub has_changes: bool,
}
#[cfg(feature = "postgres")]
pub async fn execute(
client: &Client,
config: &WaypointConfig,
target: DiffTarget,
) -> Result<DiffReport> {
let schema_name = &config.migrations.schema;
let current = schema::introspect(client, schema_name).await?;
let target_snapshot = match target {
DiffTarget::Database(ref url) => {
let target_client = crate::db::connect(url).await?;
schema::introspect(&target_client, schema_name).await?
}
};
let diffs = schema::diff(¤t, &target_snapshot);
let generated_sql = schema::generate_ddl(&diffs);
let has_changes = !diffs.is_empty();
Ok(DiffReport {
diffs,
generated_sql,
has_changes,
})
}
pub async fn execute_db(
client: &DbClient,
config: &WaypointConfig,
target: DiffTarget,
) -> Result<DiffReport> {
let schema_name = client.resolve_schema(&config.migrations.schema).await?;
let current = schema::introspect_db(client, &schema_name).await?;
let target_snapshot = match target {
DiffTarget::Database(ref url) => {
let target_client = connect_for_url(url).await?;
let target_schema = match target_client.dialect_kind() {
DialectKind::Mysql => target_client.current_database().await?,
DialectKind::Postgres => {
target_client
.resolve_schema(&config.migrations.schema)
.await?
}
};
schema::introspect_db(&target_client, &target_schema).await?
}
};
let diffs = schema::diff(¤t, &target_snapshot);
let generated_sql = schema::generate_ddl(&diffs);
let has_changes = !diffs.is_empty();
Ok(DiffReport {
diffs,
generated_sql,
has_changes,
})
}
async fn connect_for_url(url: &str) -> Result<DbClient> {
let kind = DialectKind::from_url(url).unwrap_or(DialectKind::Postgres);
match kind {
#[cfg(feature = "postgres")]
DialectKind::Postgres => {
let c = crate::db::connect(url).await?;
Ok(DbClient::with_postgres(c))
}
#[cfg(not(feature = "postgres"))]
DialectKind::Postgres => Err(WaypointError::ConfigError(
"PostgreSQL support is not compiled in".into(),
)),
#[cfg(feature = "mysql")]
DialectKind::Mysql => {
let pool = mysql_async::Pool::from_url(url)
.map_err(|e| WaypointError::ConfigError(format!("Invalid MySQL URL: {}", e)))?;
Ok(DbClient::with_mysql(pool))
}
#[cfg(not(feature = "mysql"))]
DialectKind::Mysql => Err(WaypointError::ConfigError(
"MySQL support is not compiled in".into(),
)),
}
}