lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

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 = ?", &params)?;
     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, &params)?;
     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, &params)?;
     86     Ok(())
     87 }