Skip to main content

waypoint_core/
lib.rs

1//! Lightweight, Flyway-compatible PostgreSQL migration library.
2//!
3//! # Quick Start
4//!
5//! ```rust,no_run
6//! use waypoint_core::config::WaypointConfig;
7//! use waypoint_core::Waypoint;
8//!
9//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
10//! let config = WaypointConfig::load(None, None)?;
11//! let wp = Waypoint::new(config).await?;
12//! let report = wp.migrate(None).await?;
13//! println!("Applied {} migrations", report.migrations_applied);
14//! # Ok(())
15//! # }
16//! ```
17//!
18//! # Architecture
19//!
20//! - [`config`] — Configuration loading (TOML, env vars, CLI overrides)
21//! - [`migration`] — Migration file parsing and scanning
22//! - [`db`] — Database connections, TLS, advisory locks
23//! - [`history`] — Schema history table operations
24//! - [`commands`] — Individual command implementations
25//! - [`checksum`] — CRC32 checksums (Flyway-compatible)
26//! - [`placeholder`] — `${key}` placeholder replacement in SQL
27//! - [`hooks`] — SQL callback hooks (before/after migrate)
28//! - [`directive`] — `-- waypoint:*` comment directive parsing
29//! - [`guard`] — Guard expression parser and evaluator for pre/post conditions
30//! - [`sql_parser`] — Regex-based DDL extraction
31//! - [`safety`] — Migration safety analysis (lock levels, impact, verdicts)
32//! - [`schema`] — PostgreSQL schema introspection + diff
33//! - [`dependency`] — Migration dependency graph
34//! - [`preflight`] — Pre-migration health checks
35//! - [`multi`] — Multi-database orchestration
36//! - [`error`] — Error types
37
38pub mod advisor;
39pub mod checksum;
40pub mod commands;
41pub mod config;
42pub mod db;
43pub mod dependency;
44pub mod directive;
45pub mod error;
46pub mod guard;
47pub mod history;
48pub mod hooks;
49pub mod migration;
50pub mod multi;
51pub mod placeholder;
52pub mod preflight;
53pub mod reversal;
54pub mod safety;
55pub mod schema;
56pub mod sql_parser;
57
58use std::path::PathBuf;
59
60use config::WaypointConfig;
61use error::Result;
62use tokio_postgres::Client;
63
64pub use advisor::AdvisorReport;
65pub use commands::changelog::ChangelogReport;
66pub use commands::check_conflicts::ConflictReport;
67pub use commands::diff::DiffReport;
68pub use commands::drift::DriftReport;
69pub use commands::explain::ExplainReport;
70pub use commands::info::{MigrationInfo, MigrationState};
71pub use commands::lint::LintReport;
72pub use commands::migrate::MigrateReport;
73pub use commands::repair::RepairReport;
74pub use commands::safety::SafetyCommandReport;
75pub use commands::simulate::SimulationReport;
76pub use commands::snapshot::{RestoreReport, SnapshotReport};
77pub use commands::undo::{UndoReport, UndoTarget};
78pub use commands::validate::ValidateReport;
79pub use config::CliOverrides;
80pub use multi::MultiWaypoint;
81pub use preflight::PreflightReport;
82pub use safety::SafetyReport;
83
84/// Main entry point for the Waypoint library.
85///
86/// Create a `Waypoint` instance with a config and use its methods to
87/// run migration commands programmatically.
88pub struct Waypoint {
89    pub config: WaypointConfig,
90    client: Client,
91}
92
93impl Waypoint {
94    /// Create a new Waypoint instance, connecting to the database.
95    ///
96    /// If `connect_retries` is configured, retries with exponential backoff.
97    pub async fn new(config: WaypointConfig) -> Result<Self> {
98        let conn_string = config.connection_string()?;
99        let client = db::connect_with_full_config(
100            &conn_string,
101            &config.database.ssl_mode,
102            config.database.connect_retries,
103            config.database.connect_timeout_secs,
104            config.database.statement_timeout_secs,
105            config.database.keepalive_secs,
106        )
107        .await?;
108        Ok(Self { config, client })
109    }
110
111    /// Create a new Waypoint instance with an existing database client.
112    pub fn with_client(config: WaypointConfig, client: Client) -> Self {
113        Self { config, client }
114    }
115
116    /// Get a reference to the underlying database client.
117    pub fn client(&self) -> &Client {
118        &self.client
119    }
120
121    /// Apply pending migrations.
122    pub async fn migrate(&self, target_version: Option<&str>) -> Result<MigrateReport> {
123        commands::migrate::execute(&self.client, &self.config, target_version).await
124    }
125
126    /// Show migration status information.
127    pub async fn info(&self) -> Result<Vec<MigrationInfo>> {
128        commands::info::execute(&self.client, &self.config).await
129    }
130
131    /// Validate applied migrations against local files.
132    pub async fn validate(&self) -> Result<ValidateReport> {
133        commands::validate::execute(&self.client, &self.config).await
134    }
135
136    /// Repair the schema history table.
137    pub async fn repair(&self) -> Result<RepairReport> {
138        commands::repair::execute(&self.client, &self.config).await
139    }
140
141    /// Baseline an existing database.
142    pub async fn baseline(&self, version: Option<&str>, description: Option<&str>) -> Result<()> {
143        commands::baseline::execute(&self.client, &self.config, version, description).await
144    }
145
146    /// Undo applied migrations.
147    pub async fn undo(&self, target: UndoTarget) -> Result<UndoReport> {
148        commands::undo::execute(&self.client, &self.config, target).await
149    }
150
151    /// Drop all objects in managed schemas.
152    pub async fn clean(&self, allow_clean: bool) -> Result<Vec<String>> {
153        commands::clean::execute(&self.client, &self.config, allow_clean).await
154    }
155
156    /// Run lint on migration files (no DB required).
157    pub fn lint(locations: &[PathBuf], disabled_rules: &[String]) -> Result<LintReport> {
158        commands::lint::execute(locations, disabled_rules)
159    }
160
161    /// Generate changelog from migration files (no DB required).
162    pub fn changelog(
163        locations: &[PathBuf],
164        from: Option<&str>,
165        to: Option<&str>,
166    ) -> Result<ChangelogReport> {
167        commands::changelog::execute(locations, from, to)
168    }
169
170    /// Compare database schema against a target.
171    pub async fn diff(&self, target: commands::diff::DiffTarget) -> Result<DiffReport> {
172        commands::diff::execute(&self.client, &self.config, target).await
173    }
174
175    /// Detect schema drift.
176    pub async fn drift(&self) -> Result<DriftReport> {
177        commands::drift::execute(&self.client, &self.config).await
178    }
179
180    /// Take a schema snapshot.
181    pub async fn snapshot(
182        &self,
183        snapshot_config: &commands::snapshot::SnapshotConfig,
184    ) -> Result<SnapshotReport> {
185        commands::snapshot::execute_snapshot(&self.client, &self.config, snapshot_config).await
186    }
187
188    /// Restore from a schema snapshot.
189    pub async fn restore(
190        &self,
191        snapshot_config: &commands::snapshot::SnapshotConfig,
192        snapshot_id: &str,
193    ) -> Result<RestoreReport> {
194        commands::snapshot::execute_restore(
195            &self.client,
196            &self.config,
197            snapshot_config,
198            snapshot_id,
199        )
200        .await
201    }
202
203    /// Run enhanced dry-run with EXPLAIN.
204    pub async fn explain(&self) -> Result<ExplainReport> {
205        commands::explain::execute(&self.client, &self.config).await
206    }
207
208    /// Run pre-flight health checks.
209    pub async fn preflight(&self) -> Result<PreflightReport> {
210        preflight::run_preflight(&self.client, &self.config.preflight).await
211    }
212
213    /// Check for branch conflicts (no DB required).
214    pub fn check_conflicts(locations: &[PathBuf], base_branch: &str) -> Result<ConflictReport> {
215        commands::check_conflicts::execute(locations, base_branch)
216    }
217
218    /// Analyze pending migrations for safety (lock analysis, impact estimation).
219    pub async fn safety(&self) -> Result<SafetyCommandReport> {
220        commands::safety::execute(&self.client, &self.config).await
221    }
222
223    /// Run schema advisor to suggest improvements.
224    pub async fn advise(&self) -> Result<AdvisorReport> {
225        commands::advisor::execute(&self.client, &self.config).await
226    }
227
228    /// Simulate pending migrations in a throwaway schema.
229    pub async fn simulate(&self) -> Result<SimulationReport> {
230        commands::simulate::execute(&self.client, &self.config).await
231    }
232}