rootasrole_core/database/
migration.rs1use std::error::Error;
2
3use log::debug;
4use semver::Version;
5
6use crate::version::PACKAGE_VERSION;
7
8type MigrationFn<T> = fn(&Migration<T>, &mut T) -> Result<(), Box<dyn Error>>;
9
10pub struct Migration<T> {
11 pub from: fn() -> Version,
12 pub to: fn() -> Version,
13 pub up: MigrationFn<T>,
14 pub down: MigrationFn<T>,
15}
16
17#[derive(PartialEq, Eq, Debug)]
18pub enum ChangeResult {
19 UpgradeDirect,
20 DowngradeDirect,
21 UpgradeIndirect,
22 DowngradeIndirect,
23 None,
24}
25
26impl<T> Migration<T> {
27 pub fn from(&self) -> Version {
28 (self.from)()
29 }
30 pub fn to(&self) -> Version {
31 (self.to)()
32 }
33 pub fn change(
34 &self,
35 doc: &mut T,
36 from: &Version,
37 to: &Version,
38 ) -> Result<ChangeResult, Box<dyn Error>> {
39 debug!("Checking migration from {} to {} :", self.from(), self.to());
40 debug!(
41 "
42\tself.from() == *from -> {}\tself.from() == *to -> {}
43\tself.to() == *to -> {}\tself.to() == *from -> {}
44\t*from < *to -> {}\tself.to() < *to -> {}\tself.to() > *from -> {}
45\t*from > *to -> {}\tself.from() < *to -> {}\tself.from() > *from -> {}",
46 self.from() == *from,
47 self.to() == *from,
48 self.to() == *to,
49 self.to() == *from,
50 *from < *to,
51 self.to() < *to,
52 self.to() > *from,
53 *from > *to,
54 self.from() < *to,
55 self.from() > *from
56 );
57 if self.from() == *from && self.to() == *to {
58 debug!("Direct Upgrading from {} to {}", self.from(), self.to());
59 (self.up)(self, doc)?;
60 Ok(ChangeResult::UpgradeDirect)
61 } else if self.to() == *from && self.from() == *to {
62 debug!("Direct Downgrading from {} to {}", self.to(), self.from());
63 (self.down)(self, doc)?;
64 Ok(ChangeResult::DowngradeDirect)
65 } else if *from < *to && self.from() == *from && self.to() < *to && self.to() > *from {
66 debug!("Step Upgrading from {} to {}", self.from(), self.to());
67 (self.up)(self, doc)?;
69 Ok(ChangeResult::UpgradeIndirect)
70 } else if *from > *to && self.to() == *from && self.from() > *to && self.from() < *from {
71 debug!("Step Downgrading from {} to {}", self.to(), self.from());
72 (self.down)(self, doc)?;
74 Ok(ChangeResult::DowngradeIndirect)
75 } else {
76 Ok(ChangeResult::None)
77 }
78 }
79
80 pub fn migrate_from(
81 from: &Version,
82 to: &Version,
83 doc: &mut T,
84 migrations: &[Self],
85 ) -> Result<bool, Box<dyn Error>> {
86 let mut from = from.clone();
87 let to = to.clone();
88 debug!("===== Migrating from {} to {} =====", from, to);
89 if from != to {
90 let mut migrated = ChangeResult::UpgradeIndirect;
91 while migrated == ChangeResult::UpgradeIndirect
92 || migrated == ChangeResult::DowngradeIndirect
93 {
94 migrated = ChangeResult::None;
95 for migration in migrations {
96 match migration.change(doc, &from, &to)? {
97 ChangeResult::UpgradeDirect | ChangeResult::DowngradeDirect => {
98 return Ok(true);
99 }
100 ChangeResult::UpgradeIndirect => {
101 from = migration.to();
102 migrated = ChangeResult::UpgradeIndirect;
103 break;
104 }
105 ChangeResult::DowngradeIndirect => {
106 from = migration.from();
107 migrated = ChangeResult::DowngradeIndirect;
108 break;
109 }
110 ChangeResult::None => {
111 migrated = ChangeResult::None;
112 }
113 }
114 }
115 if migrated == ChangeResult::None {
116 return Err(format!("No migration from {} to {} found", from, to).into());
117 }
118 }
119 }
120 Ok(false)
121 }
122
123 pub fn migrate(
129 version: &Version,
130 doc: &mut T,
131 migrations: &[Self],
132 ) -> Result<bool, Box<dyn Error>>
133where {
134 Self::migrate_from(
135 version,
136 &Version::parse(PACKAGE_VERSION).unwrap(),
137 doc,
138 migrations,
139 )
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use semver::Version;
147
148 #[test]
149 fn test_migration() {
150 let mut doc = 0;
151 let migrations = vec![
152 Migration {
153 from: || Version::parse("1.0.0").unwrap(),
154 to: || Version::parse("2.0.0").unwrap(),
155 up: |_, doc| {
156 *doc += 1;
157 Ok(())
158 },
159 down: |_, doc| {
160 *doc -= 1;
161 Ok(())
162 },
163 },
164 Migration {
165 from: || Version::parse("2.0.0").unwrap(),
166 to: || Version::parse("3.0.0-alpha.1").unwrap(),
167 up: |_, doc| {
168 *doc += 1;
169 Ok(())
170 },
171 down: |_, doc| {
172 *doc -= 1;
173 Ok(())
174 },
175 },
176 Migration {
177 from: || Version::parse("3.0.0-alpha.1").unwrap(),
178 to: || Version::parse(PACKAGE_VERSION).unwrap(),
179 up: |_, doc| {
180 *doc += 1;
181 Ok(())
182 },
183 down: |_, doc| {
184 *doc -= 1;
185 Ok(())
186 },
187 },
188 Migration {
189 from: || Version::parse(PACKAGE_VERSION).unwrap(),
190 to: || Version::parse("4.0.0").unwrap(),
191 up: |_, doc| {
192 *doc += 1;
193 Ok(())
194 },
195 down: |_, doc| {
196 *doc -= 1;
197 Ok(())
198 },
199 },
200 ];
201 assert_eq!(
202 Migration::migrate(&Version::parse("1.0.0").unwrap(), &mut doc, &migrations).unwrap(),
203 true
204 );
205 assert_eq!(doc, 3);
206 doc = 0;
207 assert_eq!(
208 Migration::migrate(&Version::parse("2.0.0").unwrap(), &mut doc, &migrations).unwrap(),
209 true
210 );
211 assert_eq!(doc, 2);
212 doc = 0;
213 assert_eq!(
214 Migration::migrate(
215 &Version::parse("3.0.0-alpha.1").unwrap(),
216 &mut doc,
217 &migrations
218 )
219 .unwrap(),
220 true
221 );
222 assert_eq!(doc, 1);
223 doc = 0;
224 assert_eq!(
225 Migration::migrate(&Version::parse("4.0.0").unwrap(), &mut doc, &migrations).unwrap(),
226 true
227 );
228 assert_eq!(doc, -1);
229 doc = 0;
230 assert_eq!(
231 Migration::migrate(
232 &Version::parse(PACKAGE_VERSION).unwrap(),
233 &mut doc,
234 &migrations
235 )
236 .unwrap(),
237 false
238 );
239 assert_eq!(doc, 0);
240 }
241}