waypoint_core/commands/
diff.rs1use serde::Serialize;
4
5#[cfg(feature = "postgres")]
6use tokio_postgres::Client;
7
8use crate::config::WaypointConfig;
9use crate::db::DbClient;
10use crate::dialect::DialectKind;
11use crate::error::{Result, WaypointError};
12use crate::schema::{self, SchemaDiff};
13
14pub enum DiffTarget {
16 Database(String),
18}
19
20#[derive(Debug, Serialize)]
22pub struct DiffReport {
23 pub diffs: Vec<SchemaDiff>,
25 pub generated_sql: String,
27 pub has_changes: bool,
29}
30
31#[cfg(feature = "postgres")]
33pub async fn execute(
34 client: &Client,
35 config: &WaypointConfig,
36 target: DiffTarget,
37) -> Result<DiffReport> {
38 let schema_name = &config.migrations.schema;
39
40 let current = schema::introspect(client, schema_name).await?;
41
42 let target_snapshot = match target {
43 DiffTarget::Database(ref url) => {
44 let target_client = crate::db::connect(url).await?;
45 schema::introspect(&target_client, schema_name).await?
46 }
47 };
48
49 let diffs = schema::diff(¤t, &target_snapshot);
50 let generated_sql = schema::generate_ddl(&diffs);
51 let has_changes = !diffs.is_empty();
52
53 Ok(DiffReport {
54 diffs,
55 generated_sql,
56 has_changes,
57 })
58}
59
60pub async fn execute_db(
67 client: &DbClient,
68 config: &WaypointConfig,
69 target: DiffTarget,
70) -> Result<DiffReport> {
71 let schema_name = client.resolve_schema(&config.migrations.schema).await?;
72
73 let current = schema::introspect_db(client, &schema_name).await?;
74
75 let target_snapshot = match target {
76 DiffTarget::Database(ref url) => {
77 let target_client = connect_for_url(url).await?;
78 let target_schema = match target_client.dialect_kind() {
86 DialectKind::Mysql => target_client.current_database().await?,
87 DialectKind::Postgres => {
88 target_client
89 .resolve_schema(&config.migrations.schema)
90 .await?
91 }
92 };
93 schema::introspect_db(&target_client, &target_schema).await?
94 }
95 };
96
97 let diffs = schema::diff(¤t, &target_snapshot);
98 let generated_sql = schema::generate_ddl(&diffs);
99 let has_changes = !diffs.is_empty();
100
101 Ok(DiffReport {
102 diffs,
103 generated_sql,
104 has_changes,
105 })
106}
107
108async fn connect_for_url(url: &str) -> Result<DbClient> {
109 let kind = DialectKind::from_url(url).unwrap_or(DialectKind::Postgres);
110 match kind {
111 #[cfg(feature = "postgres")]
112 DialectKind::Postgres => {
113 let c = crate::db::connect(url).await?;
114 Ok(DbClient::with_postgres(c))
115 }
116 #[cfg(not(feature = "postgres"))]
117 DialectKind::Postgres => Err(WaypointError::ConfigError(
118 "PostgreSQL support is not compiled in".into(),
119 )),
120 #[cfg(feature = "mysql")]
121 DialectKind::Mysql => {
122 let pool = mysql_async::Pool::from_url(url)
123 .map_err(|e| WaypointError::ConfigError(format!("Invalid MySQL URL: {}", e)))?;
124 Ok(DbClient::with_mysql(pool))
125 }
126 #[cfg(not(feature = "mysql"))]
127 DialectKind::Mysql => Err(WaypointError::ConfigError(
128 "MySQL support is not compiled in".into(),
129 )),
130 }
131}