use std::sync::Arc;
use serde_json::Value;
#[derive(Debug, thiserror::Error)]
pub enum PayloadError {
#[error("unsupported payload schema {stored}; current schema is {current}")]
UnsupportedVersion {
stored: u32,
current: u32,
},
#[error("payload migration failed: {0}")]
Migration(String),
}
type Migration = Arc<dyn Fn(u32, Value) -> Result<Value, PayloadError> + Send + Sync>;
#[derive(Clone)]
pub struct PayloadSchema {
version: u32,
migration: Option<Migration>,
}
impl Default for PayloadSchema {
fn default() -> Self {
Self::new(1)
}
}
impl PayloadSchema {
pub fn new(version: u32) -> Self {
Self {
version,
migration: None,
}
}
pub fn version(&self) -> u32 {
self.version
}
#[must_use]
pub fn migrate(
mut self,
migration: impl Fn(u32, Value) -> Result<Value, PayloadError> + Send + Sync + 'static,
) -> Self {
self.migration = Some(Arc::new(migration));
self
}
pub fn decode(&self, stored: u32, value: Value) -> Result<Value, PayloadError> {
if stored == self.version {
return Ok(value);
}
match &self.migration {
Some(migrate) => migrate(stored, value),
None => Err(PayloadError::UnsupportedVersion {
stored,
current: self.version,
}),
}
}
}