use super::database::Store;
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
const SCHEMA_VERSION_KEY: &str = "schema_version";
const CURRENT_VERSION: u32 = 1;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Migration {
pub version: u32,
pub description: Arc<str>,
pub applied_at: u64,
}
pub struct MigrationManager {
store: Arc<Store>,
}
impl MigrationManager {
pub fn new(store: Arc<Store>) -> Self {
Self { store }
}
pub fn get_version(&self) -> Result<u32> {
match self.store.get_metadata(SCHEMA_VERSION_KEY)? {
Some(data) => {
let version = u32::from_le_bytes(data.try_into().unwrap_or([0; 4]));
Ok(version)
}
None => Ok(0),
}
}
pub fn set_version(&self, version: u32) -> Result<()> {
self.store
.set_metadata(SCHEMA_VERSION_KEY, &version.to_le_bytes())
}
pub fn migrate(&self) -> Result<()> {
let current = self.get_version()?;
if current < CURRENT_VERSION {
tracing::info!("Migrating schema from v{} to v{}", current, CURRENT_VERSION);
for version in (current + 1)..=CURRENT_VERSION {
self.run_migration(version)?;
}
self.set_version(CURRENT_VERSION)?;
tracing::info!("Migration complete");
}
Ok(())
}
fn run_migration(&self, version: u32) -> Result<()> {
match version {
1 => self.migrate_v1(),
_ => {
tracing::warn!("Unknown migration version: {}", version);
Ok(())
}
}
}
fn migrate_v1(&self) -> Result<()> {
tracing::info!("Running migration v1: Initial schema");
Ok(())
}
}