1pub mod advisor;
44pub mod checksum;
45pub mod commands;
46pub mod config;
47pub mod db;
48pub mod dependency;
49pub mod dialect;
50pub mod directive;
51pub mod engines;
52pub mod error;
53pub mod guard;
54pub mod history;
55pub mod hooks;
56pub mod migration;
57pub mod multi;
58pub mod placeholder;
59pub mod preflight;
60pub mod reversal;
61pub mod safety;
62pub mod schema;
63pub mod sql_parser;
64
65use std::path::PathBuf;
66
67use config::WaypointConfig;
68use db::DbClient;
69use error::Result;
70
71#[cfg(feature = "postgres")]
72use tokio_postgres::Client;
73
74pub use advisor::AdvisorReport;
75pub use commands::changelog::ChangelogReport;
76pub use commands::check_conflicts::ConflictReport;
77pub use commands::diff::DiffReport;
78pub use commands::drift::DriftReport;
79pub use commands::explain::ExplainReport;
80pub use commands::info::{MigrationInfo, MigrationState};
81pub use commands::lint::LintReport;
82pub use commands::migrate::MigrateReport;
83pub use commands::repair::RepairReport;
84pub use commands::safety::SafetyCommandReport;
85pub use commands::simulate::SimulationReport;
86pub use commands::snapshot::{RestoreReport, SnapshotReport};
87pub use commands::undo::{UndoReport, UndoTarget};
88pub use commands::validate::ValidateReport;
89pub use config::CliOverrides;
90pub use dialect::{DatabaseDialect, DialectKind};
91pub use multi::MultiWaypoint;
92pub use preflight::PreflightReport;
93pub use safety::SafetyReport;
94
95pub struct Waypoint {
100 pub config: WaypointConfig,
101 client: DbClient,
102}
103
104impl Waypoint {
105 pub async fn new(config: WaypointConfig) -> Result<Self> {
111 let conn_string = config.connection_string()?;
112 let client = connect_for_url(&conn_string, &config).await?;
113 Ok(Self { config, client })
114 }
115
116 #[cfg(feature = "postgres")]
121 pub fn with_client(config: WaypointConfig, client: Client) -> Self {
122 Self {
123 config,
124 client: DbClient::with_postgres(client),
125 }
126 }
127
128 pub fn with_db_client(config: WaypointConfig, client: DbClient) -> Self {
130 Self { config, client }
131 }
132
133 pub fn client(&self) -> &DbClient {
135 &self.client
136 }
137
138 #[cfg(feature = "postgres")]
144 pub fn postgres_client(&self) -> Result<&Client> {
145 self.client.as_postgres()
146 }
147
148 pub async fn migrate(&self, target_version: Option<&str>) -> Result<MigrateReport> {
150 self.migrate_with_options(target_version, false).await
151 }
152
153 pub async fn migrate_with_options(
157 &self,
158 target_version: Option<&str>,
159 force: bool,
160 ) -> Result<MigrateReport> {
161 match self.client.dialect_kind() {
162 #[cfg(feature = "postgres")]
163 DialectKind::Postgres => {
164 commands::migrate::execute_with_options(
165 self.client.as_postgres()?,
166 &self.config,
167 target_version,
168 force,
169 )
170 .await
171 }
172 #[cfg(not(feature = "postgres"))]
173 DialectKind::Postgres => Err(error::WaypointError::ConfigError(
174 "PostgreSQL support is not compiled in (enable the `postgres` feature)".into(),
175 )),
176 #[cfg(feature = "mysql")]
177 DialectKind::Mysql => {
178 commands::migrate::execute_mysql_with_options(
179 &self.client,
180 &self.config,
181 target_version,
182 force,
183 )
184 .await
185 }
186 #[cfg(not(feature = "mysql"))]
187 DialectKind::Mysql => Err(error::WaypointError::ConfigError(
188 "MySQL support is not compiled in (enable the `mysql` feature)".into(),
189 )),
190 }
191 }
192
193 pub async fn info(&self) -> Result<Vec<MigrationInfo>> {
195 commands::info::execute_db(&self.client, &self.config).await
196 }
197
198 pub async fn validate(&self) -> Result<ValidateReport> {
200 commands::validate::execute_db(&self.client, &self.config).await
201 }
202
203 pub async fn repair(&self) -> Result<RepairReport> {
205 commands::repair::execute_db(&self.client, &self.config).await
206 }
207
208 pub async fn baseline(&self, version: Option<&str>, description: Option<&str>) -> Result<()> {
210 commands::baseline::execute_db(&self.client, &self.config, version, description).await
211 }
212
213 pub async fn undo(&self, target: UndoTarget) -> Result<UndoReport> {
215 commands::undo::execute_db(&self.client, &self.config, target).await
216 }
217
218 pub async fn clean(&self, allow_clean: bool) -> Result<Vec<String>> {
220 commands::clean::execute_db(&self.client, &self.config, allow_clean).await
221 }
222
223 pub fn lint(locations: &[PathBuf], disabled_rules: &[String]) -> Result<LintReport> {
225 commands::lint::execute(locations, disabled_rules)
226 }
227
228 pub fn changelog(
230 locations: &[PathBuf],
231 from: Option<&str>,
232 to: Option<&str>,
233 ) -> Result<ChangelogReport> {
234 commands::changelog::execute(locations, from, to)
235 }
236
237 pub async fn diff(&self, target: commands::diff::DiffTarget) -> Result<DiffReport> {
239 commands::diff::execute_db(&self.client, &self.config, target).await
240 }
241
242 pub async fn drift(&self) -> Result<DriftReport> {
244 commands::drift::execute_db(&self.client, &self.config).await
245 }
246
247 pub async fn snapshot(
249 &self,
250 snapshot_config: &commands::snapshot::SnapshotConfig,
251 ) -> Result<SnapshotReport> {
252 commands::snapshot::execute_snapshot_db(&self.client, &self.config, snapshot_config).await
253 }
254
255 pub async fn restore(
257 &self,
258 snapshot_config: &commands::snapshot::SnapshotConfig,
259 snapshot_id: &str,
260 ) -> Result<RestoreReport> {
261 commands::snapshot::execute_restore_db(
262 &self.client,
263 &self.config,
264 snapshot_config,
265 snapshot_id,
266 )
267 .await
268 }
269
270 pub async fn explain(&self) -> Result<ExplainReport> {
272 commands::explain::execute_db(&self.client, &self.config).await
273 }
274
275 pub async fn preflight(&self) -> Result<PreflightReport> {
277 preflight::run_preflight_db(&self.client, &self.config.preflight).await
278 }
279
280 pub fn check_conflicts(locations: &[PathBuf], base_branch: &str) -> Result<ConflictReport> {
282 commands::check_conflicts::execute(locations, base_branch)
283 }
284
285 pub async fn safety(&self) -> Result<SafetyCommandReport> {
287 commands::safety::execute_db(&self.client, &self.config).await
288 }
289
290 pub async fn advise(&self) -> Result<AdvisorReport> {
292 commands::advisor::execute_db(&self.client, &self.config).await
293 }
294
295 pub async fn simulate(&self) -> Result<SimulationReport> {
297 commands::simulate::execute_db(&self.client, &self.config).await
298 }
299}
300
301async fn connect_for_url(
303 conn_string: &str,
304 #[cfg_attr(not(feature = "postgres"), allow(unused_variables))] config: &WaypointConfig,
305) -> Result<DbClient> {
306 let kind = DialectKind::from_url(conn_string).unwrap_or(DialectKind::Postgres);
307 match kind {
308 #[cfg(feature = "postgres")]
309 DialectKind::Postgres => {
310 let client = db::connect_with_full_config(
311 conn_string,
312 &config.database.ssl_mode,
313 config.database.connect_retries,
314 config.database.connect_timeout_secs,
315 config.database.statement_timeout_secs,
316 config.database.keepalive_secs,
317 )
318 .await?;
319 Ok(DbClient::with_postgres(client))
320 }
321 #[cfg(not(feature = "postgres"))]
322 DialectKind::Postgres => Err(error::WaypointError::ConfigError(
323 "PostgreSQL support is not compiled in (enable the `postgres` feature)".into(),
324 )),
325 #[cfg(feature = "mysql")]
326 DialectKind::Mysql => {
327 let pool = mysql_async::Pool::from_url(conn_string).map_err(|e| {
328 error::WaypointError::ConfigError(format!("Invalid MySQL connection URL: {}", e))
329 })?;
330 Ok(DbClient::with_mysql(pool))
331 }
332 #[cfg(not(feature = "mysql"))]
333 DialectKind::Mysql => Err(error::WaypointError::ConfigError(
334 "MySQL support is not compiled in (enable the `mysql` feature)".into(),
335 )),
336 }
337}