1use crate::{error::MigrationError, value::ConfigValue};
2
3pub trait Migration: Send + Sync {
5 #[allow(clippy::wrong_self_convention)]
7 fn from_version(&self) -> u32;
8 fn to_version(&self) -> u32;
9
10 fn migrate(&self, value: ConfigValue) -> Result<ConfigValue, MigrationError>;
15}
16
17#[derive(Default)]
19pub struct MigrationChain {
20 steps: Vec<Box<dyn Migration>>,
21}
22
23impl MigrationChain {
24 #[must_use]
25 pub fn with_step(mut self, step: impl Migration + 'static) -> Self {
26 self.steps.push(Box::new(step));
27 self
28 }
29
30 pub fn run(
36 &self,
37 mut value: ConfigValue,
38 mut current_version: u32,
39 target_version: u32,
40 ) -> Result<ConfigValue, MigrationError> {
41 while current_version < target_version {
42 let step = self
43 .steps
44 .iter()
45 .find(|s| s.from_version() == current_version)
46 .ok_or(MigrationError::NoPath {
47 from: current_version,
48 to: target_version,
49 })?;
50 value = step.migrate(value)?;
51 current_version = step.to_version();
52 }
53 Ok(value)
54 }
55}
56
57pub fn rename_key(value: &mut ConfigValue, from: &str, to: &str) {
59 if let ConfigValue::Table(table) = value
60 && let Some(v) = table.remove(from)
61 {
62 table.insert(to.to_owned(), v);
63 }
64}