diff options
| author | mo khan <mo@mokhan.ca> | 2025-06-11 20:20:04 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-06-11 20:20:04 -0600 |
| commit | c28b7088b6fad045060a52b6e1a2249e876090e3 (patch) | |
| tree | a8fc26fd5365d4988d9206b32d94f51047cf0bcc /src/migrations.rs | |
| parent | 19ca22e604f9bcdf6b25f973f81b2486b0dcb789 (diff) | |
refactor: extract domain model
Diffstat (limited to 'src/migrations.rs')
| -rw-r--r-- | src/migrations.rs | 103 |
1 files changed, 52 insertions, 51 deletions
diff --git a/src/migrations.rs b/src/migrations.rs index 61c5b19..021d525 100644 --- a/src/migrations.rs +++ b/src/migrations.rs @@ -1,40 +1,31 @@ +use crate::migration_discovery::{Migration, RuntimeMigrationDiscovery}; use anyhow::Result; use rusqlite::Connection; - -pub struct Migration { - pub version: i32, - pub name: &'static str, - pub sql: &'static str, -} - -const MIGRATIONS: &[Migration] = &[ - Migration { - version: 1, - name: "initial_schema", - sql: include_str!("../migrations/001_initial_schema.sql"), - }, - // Add more migrations here as needed - // Migration { - // version: 2, - // name: "add_user_table", - // sql: include_str!("../migrations/002_add_user_table.sql"), - // }, -]; +use std::path::Path; pub struct MigrationRunner<'a> { conn: &'a Connection, + discovery: RuntimeMigrationDiscovery, } impl<'a> MigrationRunner<'a> { - pub fn new(conn: &'a Connection) -> Self { - Self { conn } + pub fn new(conn: &'a Connection) -> Result<Self> { + // Default to migrations directory relative to project root + let migrations_dir = Path::new("migrations"); + let discovery = RuntimeMigrationDiscovery::new(migrations_dir)?; + Ok(Self { conn, discovery }) + } + + pub fn new_with_path<P: AsRef<Path>>(conn: &'a Connection, migrations_dir: P) -> Result<Self> { + let discovery = RuntimeMigrationDiscovery::new(migrations_dir)?; + Ok(Self { conn, discovery }) } pub fn run_migrations(&self) -> Result<()> { // Create migrations table if it doesn't exist self.conn.execute( "CREATE TABLE IF NOT EXISTS schema_migrations ( - version INTEGER PRIMARY KEY, + version BIGINT PRIMARY KEY, name TEXT NOT NULL, applied_at TEXT NOT NULL )", @@ -46,22 +37,23 @@ impl<'a> MigrationRunner<'a> { println!("Current database version: {}", current_version); - // Run pending migrations - for migration in MIGRATIONS { - if migration.version > current_version { - println!( - "Running migration {}: {}", - migration.version, migration.name - ); - self.run_migration(migration)?; - } + // Get pending migrations from discovery system + let pending_migrations = self.discovery.get_pending_migrations(current_version); + + // Run each pending migration + for migration in pending_migrations { + println!( + "Running migration {}: {}", + migration.version, migration.name + ); + self.run_migration(migration)?; } println!("All migrations completed successfully"); Ok(()) } - fn get_current_version(&self) -> Result<i32> { + fn get_current_version(&self) -> Result<i64> { // Check if schema_migrations table exists first let table_exists = self.conn.query_row( "SELECT name FROM sqlite_master WHERE type='table' AND name='schema_migrations'", @@ -75,7 +67,7 @@ impl<'a> MigrationRunner<'a> { let version = self.conn.query_row( "SELECT COALESCE(MAX(version), 0) FROM schema_migrations", [], - |row| row.get::<_, i32>(0), + |row| row.get::<_, i64>(0), )?; Ok(version) } @@ -88,14 +80,14 @@ impl<'a> MigrationRunner<'a> { fn run_migration(&self, migration: &Migration) -> Result<()> { // Execute the migration SQL - self.conn.execute_batch(migration.sql)?; + self.conn.execute_batch(&migration.sql)?; // Record the migration as applied self.conn.execute( "INSERT INTO schema_migrations (version, name, applied_at) VALUES (?1, ?2, ?3)", [ &migration.version.to_string(), - migration.name, + &migration.name, &chrono::Utc::now().to_rfc3339(), ], )?; @@ -103,7 +95,7 @@ impl<'a> MigrationRunner<'a> { Ok(()) } - pub fn rollback_to_version(&self, target_version: i32) -> Result<()> { + pub fn rollback_to_version(&self, target_version: i64) -> Result<()> { println!("Rolling back to version {}", target_version); // This is a simplified rollback - in practice you'd need down migrations @@ -127,7 +119,7 @@ impl<'a> MigrationRunner<'a> { let migrations = stmt.query_map([], |row| { Ok(( - row.get::<_, i32>(0)?, + row.get::<_, i64>(0)?, row.get::<_, String>(1)?, row.get::<_, String>(2)?, )) @@ -143,13 +135,12 @@ impl<'a> MigrationRunner<'a> { // Show pending migrations let current_version = self.get_current_version()?; - for migration in MIGRATIONS { - if migration.version > current_version { - println!( - "⏳ Migration {}: {} (pending)", - migration.version, migration.name - ); - } + let pending_migrations = self.discovery.get_pending_migrations(current_version); + for migration in pending_migrations { + println!( + "⏳ Migration {}: {} (pending)", + migration.version, migration.name + ); } Ok(()) @@ -162,8 +153,21 @@ mod tests { #[test] fn test_migration_runner() { + use std::fs; + use tempfile::TempDir; + + // Create a temporary directory with a test migration + let temp_dir = TempDir::new().unwrap(); + let migrations_dir = temp_dir.path(); + + fs::write( + migrations_dir.join("20231201120000_test_migration.sql"), + "CREATE TABLE test_table (id INTEGER PRIMARY KEY);", + ) + .unwrap(); + let conn = Connection::open_in_memory().unwrap(); - let runner = MigrationRunner::new(&conn); + let runner = MigrationRunner::new_with_path(&conn, migrations_dir).unwrap(); // Should start with version 0 assert_eq!(runner.get_current_version().unwrap(), 0); @@ -171,10 +175,7 @@ mod tests { // Run migrations runner.run_migrations().unwrap(); - // Should now be at latest version - assert_eq!( - runner.get_current_version().unwrap(), - MIGRATIONS.len() as i32 - ); + // Should now be at latest version (timestamp of our migration) + assert_eq!(runner.get_current_version().unwrap(), 20231201120000); } } |
