lib_migrations_core/
lib.rs1mod error;
2mod phase;
3mod runner;
4mod store;
5
6pub use error::{Error, Result};
7pub use phase::Phase;
8pub use runner::{DryRunPlan, DryRunResult, MigrationRunner, MigrationStatus};
9pub use store::{MemoryStore, MigrationRecord, MigrationStore};
10
11pub trait Migration<Ctx>: Send + Sync {
23 fn version(&self) -> u64;
25
26 fn name(&self) -> &str;
28
29 fn phase(&self) -> Phase {
31 Phase::PreDeploy
32 }
33
34 fn apply(&self, ctx: &mut Ctx) -> Result<()>;
36
37 fn rollback(&self, ctx: &mut Ctx) -> Result<()> {
39 let _ = ctx;
40 Err(Error::RollbackNotSupported(self.version()))
41 }
42
43 fn can_rollback(&self) -> bool {
45 false
46 }
47}
48
49use std::marker::PhantomData;
50
51pub struct FnMigration<Ctx, F, R>
53where
54 F: Fn(&mut Ctx) -> Result<()> + Send + Sync,
55 R: Fn(&mut Ctx) -> Result<()> + Send + Sync,
56{
57 version: u64,
58 name: String,
59 phase: Phase,
60 apply_fn: F,
61 rollback_fn: Option<R>,
62 _phantom: PhantomData<fn(&mut Ctx)>, }
64
65impl<Ctx, F> FnMigration<Ctx, F, fn(&mut Ctx) -> Result<()>>
66where
67 F: Fn(&mut Ctx) -> Result<()> + Send + Sync,
68{
69 pub fn new(version: u64, name: impl Into<String>, apply_fn: F) -> Self {
70 Self {
71 version,
72 name: name.into(),
73 phase: Phase::PreDeploy,
74 apply_fn,
75 rollback_fn: None,
76 _phantom: PhantomData,
77 }
78 }
79}
80
81impl<Ctx, F, R> FnMigration<Ctx, F, R>
82where
83 F: Fn(&mut Ctx) -> Result<()> + Send + Sync,
84 R: Fn(&mut Ctx) -> Result<()> + Send + Sync,
85{
86 pub fn phase(mut self, phase: Phase) -> Self {
88 self.phase = phase;
89 self
90 }
91
92 pub fn with_rollback<R2>(self, rollback_fn: R2) -> FnMigration<Ctx, F, R2>
93 where
94 R2: Fn(&mut Ctx) -> Result<()> + Send + Sync,
95 {
96 FnMigration {
97 version: self.version,
98 name: self.name,
99 phase: self.phase,
100 apply_fn: self.apply_fn,
101 rollback_fn: Some(rollback_fn),
102 _phantom: PhantomData,
103 }
104 }
105}
106
107impl<Ctx, F, R> Migration<Ctx> for FnMigration<Ctx, F, R>
108where
109 F: Fn(&mut Ctx) -> Result<()> + Send + Sync,
110 R: Fn(&mut Ctx) -> Result<()> + Send + Sync,
111{
112 fn version(&self) -> u64 {
113 self.version
114 }
115
116 fn name(&self) -> &str {
117 &self.name
118 }
119
120 fn phase(&self) -> Phase {
121 self.phase
122 }
123
124 fn apply(&self, ctx: &mut Ctx) -> Result<()> {
125 (self.apply_fn)(ctx)
126 }
127
128 fn rollback(&self, ctx: &mut Ctx) -> Result<()> {
129 match &self.rollback_fn {
130 Some(f) => f(ctx),
131 None => Err(Error::RollbackNotSupported(self.version)),
132 }
133 }
134
135 fn can_rollback(&self) -> bool {
136 self.rollback_fn.is_some()
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn test_fn_migration() {
146 let migration = FnMigration::new(1, "test", |ctx: &mut Vec<i32>| {
147 ctx.push(1);
148 Ok(())
149 });
150
151 assert_eq!(migration.version(), 1);
152 assert_eq!(migration.name(), "test");
153 assert!(!migration.can_rollback());
154
155 let mut ctx = vec![];
156 migration.apply(&mut ctx).unwrap();
157 assert_eq!(ctx, vec![1]);
158 }
159
160 #[test]
161 fn test_fn_migration_with_rollback() {
162 let migration = FnMigration::new(1, "test", |ctx: &mut Vec<i32>| {
163 ctx.push(1);
164 Ok(())
165 })
166 .with_rollback(|ctx: &mut Vec<i32>| {
167 ctx.pop();
168 Ok(())
169 });
170
171 assert!(migration.can_rollback());
172
173 let mut ctx = vec![];
174 migration.apply(&mut ctx).unwrap();
175 assert_eq!(ctx, vec![1]);
176
177 migration.rollback(&mut ctx).unwrap();
178 assert!(ctx.is_empty());
179 }
180}