mod add;
pub mod export;
mod get;
pub mod import;
mod list;
mod remove;
mod run;
pub mod types;
pub use add::AddMigrationTool;
pub use export::ExportMigrationsTool;
pub use get::GetMigrationTool;
pub use import::ImportMigrationsTool;
pub use list::ListMigrationsTool;
pub use remove::RemoveMigrationTool;
pub use run::RunMigrationsTool;
pub use types::{Migration, MigrationStatusFilter};
use crate::sqlite::error::SqliteToolError;
use chrono::Utc;
use rusqlite::Connection;
use sha2::{Digest, Sha256};
pub const MIGRATIONS_TABLE: &str = "_schema_migrations";
pub fn ensure_migrations_table(conn: &Connection) -> Result<(), SqliteToolError> {
conn.execute_batch(&format!(
r#"
CREATE TABLE IF NOT EXISTS {MIGRATIONS_TABLE} (
version TEXT PRIMARY KEY,
name TEXT NOT NULL,
sql TEXT NOT NULL,
applied_at TEXT,
checksum TEXT NOT NULL
);
"#
))?;
Ok(())
}
pub fn generate_version() -> String {
Utc::now().format("%Y%m%d_%H%M%S_%6f").to_string()
}
pub fn compute_checksum(sql: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(sql.as_bytes());
let result = hasher.finalize();
hex::encode(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_version_format() {
let version = generate_version();
assert_eq!(version.len(), 22);
assert_eq!(&version[8..9], "_");
assert_eq!(&version[15..16], "_");
}
#[test]
fn test_generate_version_uniqueness() {
let v1 = generate_version();
std::thread::sleep(std::time::Duration::from_micros(10));
let v2 = generate_version();
assert_ne!(v1, v2);
}
#[test]
fn test_compute_checksum() {
let sql = "CREATE TABLE users (id INTEGER PRIMARY KEY);";
let checksum = compute_checksum(sql);
assert_eq!(checksum.len(), 64);
assert_eq!(checksum, compute_checksum(sql));
let other = compute_checksum("CREATE TABLE posts (id INTEGER PRIMARY KEY);");
assert_ne!(checksum, other);
}
#[test]
fn test_ensure_migrations_table() {
let conn = Connection::open_in_memory().unwrap();
ensure_migrations_table(&conn).unwrap();
let count: i64 = conn
.query_row(
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?",
[MIGRATIONS_TABLE],
|row| row.get(0),
)
.unwrap();
assert_eq!(count, 1);
ensure_migrations_table(&conn).unwrap();
}
}