bottle_orm/
migration.rs

1use crate::{database::Database, model::Model};
2use futures::future::BoxFuture;
3
4/// Type alias for migration tasks (e.g., Create Table, Add Foreign Key).
5///
6/// These tasks are closures that take a `Database` connection and return a future.
7pub type MigrationTask = Box<dyn Fn(Database) -> BoxFuture<'static, Result<(), sqlx::Error>> + Send + Sync>;
8
9/// Schema migration manager.
10///
11/// Handles the registration of models and executes table creation and relationship setup in order.
12pub struct Migrator<'a> {
13    pub(crate) db: &'a Database,
14    pub(crate) tasks: Vec<MigrationTask>,
15    pub(crate) fk_task: Vec<MigrationTask>,
16}
17
18impl<'a> Migrator<'a> {
19    /// Creates a new Migrator instance associated with a Database.
20    pub fn new(db: &'a Database) -> Self {
21        Self { db, tasks: Vec::new(), fk_task: Vec::new() }
22    }
23
24    /// Registers a Model for migration.
25    ///
26    /// This queues tasks to:
27    /// 1. Create the table for the model.
28    /// 2. Assign foreign keys (executed later to ensure all tables exist).
29    ///
30    /// # Example
31    ///
32    /// ```rust,ignore
33    /// db.migrator()
34    ///   .register::<User>()
35    ///   .register::<Post>()
36    ///   .run()
37    ///   .await?;
38    /// ```
39    pub fn register<T>(mut self) -> Self
40    where
41        T: Model + 'static + Send + Sync,
42    {
43        let task = Box::new(|db: Database| -> BoxFuture<'static, Result<(), sqlx::Error>> { 
44            Box::pin(async move {
45                db.create_table::<T>().await?;
46                Ok(())
47            })
48        });
49
50        let fk_task = Box::new(|db: Database| -> BoxFuture<'static, Result<(), sqlx::Error>> {
51            Box::pin(async move {
52                db.assign_foreign_keys::<T>().await?;
53                Ok(())
54            })
55        });
56        self.tasks.push(task);
57        self.fk_task.push(fk_task);
58        self
59    }
60
61    /// Executes all registered migration tasks.
62    ///
63    /// The process follows two steps:
64    /// 1. Creates all tables (executing standard migration tasks).
65    /// 2. Creates all foreign keys (executing foreign key tasks).
66    pub async fn run(self) -> Result<Database, sqlx::Error> {
67        for task in self.tasks {
68            (task)(self.db.clone()).await?;
69        }
70
71        for task in self.fk_task {
72            (task)(self.db.clone()).await?;
73        }
74        Ok(self.db.clone())
75    }
76}