1mod queries;
2
3mod exec;
4use exec::*;
5
6mod with_self;
7pub use with_self::*;
8
9use std::fmt::Display;
10use tracing::info;
11
12use super::{IntoSchemaManagerConnection, MigrationTrait, SchemaManager, seaql_migrations};
13use sea_orm::sea_query::IntoIden;
14use sea_orm::{ConnectionTrait, DbErr, DynIden};
15
16#[derive(Copy, Clone, Debug, PartialEq, Eq)]
17pub enum MigrationStatus {
19 Pending,
21 Applied,
23}
24
25impl Display for MigrationStatus {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 let status = match self {
28 MigrationStatus::Pending => "Pending",
29 MigrationStatus::Applied => "Applied",
30 };
31 write!(f, "{status}")
32 }
33}
34
35pub struct Migration {
36 migration: Box<dyn MigrationTrait>,
37 status: MigrationStatus,
38}
39
40impl Migration {
41 pub fn name(&self) -> &str {
43 self.migration.name()
44 }
45
46 pub fn status(&self) -> MigrationStatus {
48 self.status
49 }
50}
51
52#[async_trait::async_trait]
54pub trait MigratorTrait: Send {
55 fn migrations() -> Vec<Box<dyn MigrationTrait>>;
57
58 fn migration_table_name() -> DynIden {
60 seaql_migrations::Entity.into_iden()
61 }
62
63 fn get_migration_files() -> Vec<Migration> {
65 Self::migrations()
66 .into_iter()
67 .map(|migration| Migration {
68 migration,
69 status: MigrationStatus::Pending,
70 })
71 .collect()
72 }
73
74 async fn get_migration_models<C>(db: &C) -> Result<Vec<seaql_migrations::Model>, DbErr>
76 where
77 C: ConnectionTrait,
78 {
79 Self::install(db).await?;
80 get_migration_models(db, Self::migration_table_name()).await
81 }
82
83 async fn get_migration_with_status<C>(db: &C) -> Result<Vec<Migration>, DbErr>
85 where
86 C: ConnectionTrait,
87 {
88 Self::install(db).await?;
89 get_migration_with_status(
90 Self::get_migration_files(),
91 Self::get_migration_models(db).await?,
92 )
93 }
94
95 async fn get_pending_migrations<C>(db: &C) -> Result<Vec<Migration>, DbErr>
97 where
98 C: ConnectionTrait,
99 {
100 Self::install(db).await?;
101 Ok(Self::get_migration_with_status(db)
102 .await?
103 .into_iter()
104 .filter(|file| file.status == MigrationStatus::Pending)
105 .collect())
106 }
107
108 async fn get_applied_migrations<C>(db: &C) -> Result<Vec<Migration>, DbErr>
110 where
111 C: ConnectionTrait,
112 {
113 Self::install(db).await?;
114 Ok(Self::get_migration_with_status(db)
115 .await?
116 .into_iter()
117 .filter(|file| file.status == MigrationStatus::Applied)
118 .collect())
119 }
120
121 async fn get_migration_with_status_read_only<C>(db: &C) -> Result<Vec<Migration>, DbErr>
128 where
129 C: ConnectionTrait,
130 {
131 get_migration_with_status(
132 Self::get_migration_files(),
133 get_migration_models_read_only(db, Self::migration_table_name()).await?,
134 )
135 }
136
137 async fn get_pending_migrations_read_only<C>(db: &C) -> Result<Vec<Migration>, DbErr>
142 where
143 C: ConnectionTrait,
144 {
145 Ok(Self::get_migration_with_status_read_only(db)
146 .await?
147 .into_iter()
148 .filter(|file| file.status == MigrationStatus::Pending)
149 .collect())
150 }
151
152 async fn get_applied_migrations_read_only<C>(db: &C) -> Result<Vec<Migration>, DbErr>
157 where
158 C: ConnectionTrait,
159 {
160 Ok(Self::get_migration_with_status_read_only(db)
161 .await?
162 .into_iter()
163 .filter(|file| file.status == MigrationStatus::Applied)
164 .collect())
165 }
166
167 async fn install<C>(db: &C) -> Result<(), DbErr>
169 where
170 C: ConnectionTrait,
171 {
172 install(db, Self::migration_table_name()).await
173 }
174
175 async fn status<C>(db: &C) -> Result<(), DbErr>
177 where
178 C: ConnectionTrait,
179 {
180 Self::install(db).await?;
181
182 info!("Checking migration status");
183
184 for Migration { migration, status } in Self::get_migration_with_status(db).await? {
185 info!("Migration '{}'... {}", migration.name(), status);
186 }
187
188 Ok(())
189 }
190
191 async fn fresh<'c, C>(db: C) -> Result<(), DbErr>
193 where
194 C: IntoSchemaManagerConnection<'c>,
195 {
196 let db = db.into_database_executor();
197 let manager = SchemaManager::new(db);
198 exec_fresh::<Self>(&manager).await
199 }
200
201 async fn refresh<'c, C>(db: C) -> Result<(), DbErr>
203 where
204 C: IntoSchemaManagerConnection<'c>,
205 {
206 let db = db.into_database_executor();
207 let manager = SchemaManager::new(db);
208 exec_down::<Self>(&manager, None).await?;
209 exec_up::<Self>(&manager, None).await
210 }
211
212 async fn reset<'c, C>(db: C) -> Result<(), DbErr>
214 where
215 C: IntoSchemaManagerConnection<'c>,
216 {
217 let db = db.into_database_executor();
218 let manager = SchemaManager::new(db);
219 exec_down::<Self>(&manager, None).await?;
220 uninstall(&manager, Self::migration_table_name()).await
221 }
222
223 async fn uninstall<'c, C>(db: C) -> Result<(), DbErr>
226 where
227 C: IntoSchemaManagerConnection<'c>,
228 {
229 let db = db.into_database_executor();
230 let manager = SchemaManager::new(db);
231 uninstall(&manager, Self::migration_table_name()).await
232 }
233
234 async fn up<'c, C>(db: C, steps: Option<u32>) -> Result<(), DbErr>
236 where
237 C: IntoSchemaManagerConnection<'c>,
238 {
239 let db = db.into_database_executor();
240 let manager = SchemaManager::new(db);
241 exec_up::<Self>(&manager, steps).await
242 }
243
244 async fn down<'c, C>(db: C, steps: Option<u32>) -> Result<(), DbErr>
246 where
247 C: IntoSchemaManagerConnection<'c>,
248 {
249 let db = db.into_database_executor();
250 let manager = SchemaManager::new(db);
251 exec_down::<Self>(&manager, steps).await
252 }
253}
254
255async fn exec_fresh<M>(manager: &SchemaManager<'_>) -> Result<(), DbErr>
256where
257 M: MigratorTrait + ?Sized,
258{
259 let db = manager.get_connection();
260
261 M::install(db).await?;
262
263 drop_everything(db).await?;
264
265 exec_up::<M>(manager, None).await
266}
267
268async fn exec_up<M>(manager: &SchemaManager<'_>, steps: Option<u32>) -> Result<(), DbErr>
269where
270 M: MigratorTrait + ?Sized,
271{
272 let db = manager.get_connection();
273
274 M::install(db).await?;
275
276 exec_up_with(
277 manager,
278 steps,
279 M::get_pending_migrations(db).await?,
280 M::migration_table_name(),
281 )
282 .await
283}
284
285async fn exec_down<M>(manager: &SchemaManager<'_>, steps: Option<u32>) -> Result<(), DbErr>
286where
287 M: MigratorTrait + ?Sized,
288{
289 let db = manager.get_connection();
290
291 M::install(db).await?;
292
293 exec_down_with(
294 manager,
295 steps,
296 M::get_applied_migrations(db).await?,
297 M::migration_table_name(),
298 )
299 .await
300}