migrations.rs (2489B)
1 use alloc::string::ToString; 2 use alloc::vec::Vec; 3 4 use crate::SqlExecutor; 5 use crate::error::SqlError; 6 use serde_json::{Value, json}; 7 8 #[derive(Clone, Copy, Debug)] 9 pub struct Migration { 10 pub name: &'static str, 11 pub up_sql: &'static str, 12 pub down_sql: &'static str, 13 } 14 15 pub fn migrations_run_all_up<E>(executor: &E, migrations: &[Migration]) -> Result<(), SqlError> 16 where 17 E: SqlExecutor, 18 { 19 ensure_table(executor)?; 20 for migration in migrations { 21 if !is_applied(executor, migration.name)? { 22 executor.begin()?; 23 let result = (|| -> Result<(), SqlError> { 24 let _ = executor.exec(migration.up_sql, "[]")?; 25 mark_applied(executor, migration.name)?; 26 Ok(()) 27 })(); 28 match result { 29 Ok(()) => { 30 executor.commit()?; 31 } 32 Err(err) => { 33 let _ = executor.rollback(); 34 return Err(err); 35 } 36 } 37 } 38 } 39 Ok(()) 40 } 41 42 pub fn migrations_run_all_down<E>(executor: &E, migrations: &[Migration]) -> Result<(), SqlError> 43 where 44 E: SqlExecutor, 45 { 46 ensure_table(executor)?; 47 executor.begin()?; 48 for migration in migrations.iter().rev() { 49 let params = json!([migration.name]).to_string(); 50 let _ = executor.exec("delete from __migrations where name = ?", ¶ms)?; 51 let _ = executor.exec(migration.down_sql, "[]")?; 52 } 53 executor.commit()?; 54 Ok(()) 55 } 56 57 fn ensure_table<E>(executor: &E) -> Result<(), SqlError> 58 where 59 E: SqlExecutor, 60 { 61 let _ = executor.exec("create table if not exists __migrations(id integer primary key, name text not null unique, applied_at text not null default (datetime('now')))", "[]")?; 62 Ok(()) 63 } 64 65 fn is_applied<E>(executor: &E, name: &str) -> Result<bool, SqlError> 66 where 67 E: SqlExecutor, 68 { 69 let params = json!([name]).to_string(); 70 let sql = "select 1 as applied from __migrations where name = ? limit 1"; 71 let json = executor.query_raw(sql, ¶ms)?; 72 if json.trim().is_empty() { 73 return Ok(false); 74 } 75 let rows: Vec<Value> = serde_json::from_str(&json)?; 76 Ok(!rows.is_empty()) 77 } 78 79 fn mark_applied<E>(executor: &E, name: &str) -> Result<(), SqlError> 80 where 81 E: SqlExecutor, 82 { 83 let params = json!([name]).to_string(); 84 let sql = "insert or ignore into __migrations(name) values(?)"; 85 let _ = executor.exec(sql, ¶ms)?; 86 Ok(()) 87 }