use serde::{Deserialize, Serialize};
pub mod dir_storage;
pub mod errors;
pub mod forward;
mod migrator;
pub mod storage;
pub mod versioned_dir;
pub mod versioned_file;
pub use version_migrate_macro::Versioned;
#[doc(inline)]
pub use version_migrate_macro::Queryable as DeriveQueryable;
#[doc(inline)]
pub use version_migrate_macro::VersionMigrate;
#[macro_export]
macro_rules! migrate_path {
($entity:expr, [$first:ty, $($rest:ty),+ $(,)?]) => {
$crate::migrator_vec_helper!($first; $($rest),+; $entity)
};
($entity:expr, [$first:ty, $($rest:ty),+ $(,)?], version_key = $version_key:expr, data_key = $data_key:expr) => {
$crate::migrator_vec_helper_with_keys!($first; $($rest),+; $entity; $version_key; $data_key)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_helper {
($first:ty; $last:ty; $entity:expr) => {
$crate::Migrator::define($entity)
.from::<$first>()
.into::<$last>()
};
($first:ty; $second:ty, $($rest:ty),+; $entity:expr) => {
$crate::migrator_vec_build_steps!($first; $($rest),+; $entity; {
$crate::Migrator::define($entity).from::<$first>().step::<$second>()
})
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_build_steps {
($first:ty; $last:ty; $entity:expr; { $builder:expr }) => {
$builder.into::<$last>()
};
($first:ty; $current:ty, $($rest:ty),+; $entity:expr; { $builder:expr }) => {
$crate::migrator_vec_build_steps!($first; $($rest),+; $entity; {
$builder.step::<$current>()
})
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_helper_with_keys {
($first:ty; $last:ty; $entity:expr; $version_key:expr; $data_key:expr) => {
$crate::Migrator::define($entity)
.with_keys($version_key, $data_key)
.from::<$first>()
.into::<$last>()
};
($first:ty; $second:ty, $($rest:ty),+; $entity:expr; $version_key:expr; $data_key:expr) => {
$crate::migrator_vec_build_steps_with_keys!($first; $($rest),+; $entity; $version_key; $data_key; {
$crate::Migrator::define($entity).with_keys($version_key, $data_key).from::<$first>().step::<$second>()
})
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_build_steps_with_keys {
($first:ty; $last:ty; $entity:expr; $version_key:expr; $data_key:expr; { $builder:expr }) => {
$builder.into::<$last>()
};
($first:ty; $current:ty, $($rest:ty),+; $entity:expr; $version_key:expr; $data_key:expr; { $builder:expr }) => {
$crate::migrator_vec_build_steps_with_keys!($first; $($rest),+; $entity; $version_key; $data_key; {
$builder.step::<$current>()
})
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_helper_with_save {
($first:ty; $last:ty; $entity:expr) => {
$crate::Migrator::define($entity)
.from::<$first>()
.into_with_save::<$last>()
};
($first:ty; $second:ty, $($rest:ty),+; $entity:expr) => {
$crate::migrator_vec_build_steps_with_save!($first; $($rest),+; $entity; {
$crate::Migrator::define($entity).from::<$first>().step::<$second>()
})
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_build_steps_with_save {
($first:ty; $last:ty; $entity:expr; { $builder:expr }) => {
$builder.into_with_save::<$last>()
};
($first:ty; $current:ty, $($rest:ty),+; $entity:expr; { $builder:expr }) => {
$crate::migrator_vec_build_steps_with_save!($first; $($rest),+; $entity; {
$builder.step::<$current>()
})
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_helper_with_keys_and_save {
($first:ty; $last:ty; $entity:expr; $version_key:expr; $data_key:expr) => {
$crate::Migrator::define($entity)
.with_keys($version_key, $data_key)
.from::<$first>()
.into_with_save::<$last>()
};
($first:ty; $second:ty, $($rest:ty),+; $entity:expr; $version_key:expr; $data_key:expr) => {
$crate::migrator_vec_build_steps_with_keys_and_save!($first; $($rest),+; $entity; $version_key; $data_key; {
$crate::Migrator::define($entity).with_keys($version_key, $data_key).from::<$first>().step::<$second>()
})
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! migrator_vec_build_steps_with_keys_and_save {
($first:ty; $last:ty; $entity:expr; $version_key:expr; $data_key:expr; { $builder:expr }) => {
$builder.into_with_save::<$last>()
};
($first:ty; $current:ty, $($rest:ty),+; $entity:expr; $version_key:expr; $data_key:expr; { $builder:expr }) => {
$crate::migrator_vec_build_steps_with_keys_and_save!($first; $($rest),+; $entity; $version_key; $data_key; {
$builder.step::<$current>()
})
};
}
#[macro_export]
macro_rules! migrator {
($entity:expr => [$first:ty, $($rest:ty),+ $(,)?], version_key = $version_key:expr, data_key = $data_key:expr, save = true) => {{
let mut migrator = $crate::Migrator::new();
let path = $crate::migrator_vec_helper_with_keys_and_save!($first; $($rest),+; $entity; $version_key; $data_key);
migrator.register(path).map(|_| migrator)
}};
($entity:expr => [$first:ty, $($rest:ty),+ $(,)?], version_key = $version_key:expr, data_key = $data_key:expr) => {{
let mut migrator = $crate::Migrator::new();
let path = $crate::migrate_path!($entity, [$first, $($rest),+], version_key = $version_key, data_key = $data_key);
migrator.register(path).map(|_| migrator)
}};
(@keys version_key = $version_key:expr, data_key = $data_key:expr, save = true; $($entity:expr => [$first:ty, $($rest:ty),+ $(,)?]),+ $(,)?) => {{
let mut migrator = $crate::Migrator::new();
$(
let path = $crate::migrator_vec_helper_with_keys_and_save!($first; $($rest),+; $entity; $version_key; $data_key);
migrator.register(path)?;
)+
Ok::<$crate::Migrator, $crate::MigrationError>(migrator)
}};
(@keys version_key = $version_key:expr, data_key = $data_key:expr; $($entity:expr => [$first:ty, $($rest:ty),+ $(,)?]),+ $(,)?) => {{
let mut migrator = $crate::Migrator::new();
$(
let path = $crate::migrate_path!($entity, [$first, $($rest),+], version_key = $version_key, data_key = $data_key);
migrator.register(path)?;
)+
Ok::<$crate::Migrator, $crate::MigrationError>(migrator)
}};
($entity:expr => [$first:ty, $($rest:ty),+ $(,)?], save = true) => {{
let mut migrator = $crate::Migrator::new();
let path = $crate::migrator_vec_helper_with_save!($first; $($rest),+; $entity);
migrator.register(path).map(|_| migrator)
}};
($entity:expr => [$first:ty, $($rest:ty),+ $(,)?]) => {{
let mut migrator = $crate::Migrator::new();
let path = $crate::migrate_path!($entity, [$first, $($rest),+]);
migrator.register(path).map(|_| migrator)
}};
(@save; $($entity:expr => [$first:ty, $($rest:ty),+ $(,)?]),+ $(,)?) => {{
let mut migrator = $crate::Migrator::new();
$(
let path = $crate::migrator_vec_helper_with_save!($first; $($rest),+; $entity);
migrator.register(path)?;
)+
Ok::<$crate::Migrator, $crate::MigrationError>(migrator)
}};
($($entity:expr => [$first:ty, $($rest:ty),+ $(,)?]),+ $(,)?) => {{
let mut migrator = $crate::Migrator::new();
$(
let path = $crate::migrate_path!($entity, [$first, $($rest),+]);
migrator.register(path)?;
)+
Ok::<$crate::Migrator, $crate::MigrationError>(migrator)
}};
}
pub use errors::{IoOperationKind, MigrationError, StoreError};
pub use migrator::{ConfigMigrator, MigrationPath, Migrator};
pub use local_store::{AtomicWriteConfig, FileStorageStrategy, FormatStrategy, LoadBehavior};
pub use storage::FileStorage;
pub use dir_storage::DirStorage;
pub use local_store::{DirStorageStrategy, FilenameEncoding};
#[cfg(feature = "async")]
pub use dir_storage::AsyncDirStorage;
pub use versioned_dir::VersionedDirStorage;
pub use versioned_file::VersionedFileStorage;
#[cfg(feature = "async")]
pub use versioned_dir::VersionedAsyncDirStorage;
pub use forward::{ForwardContext, Forwardable};
pub use local_store::{AppPaths, PathStrategy, PrefPath};
#[cfg(feature = "async")]
pub use async_trait::async_trait;
pub trait Versioned {
const VERSION: &'static str;
const VERSION_KEY: &'static str = "version";
const DATA_KEY: &'static str = "data";
}
pub trait MigratesTo<T: Versioned>: Versioned {
fn migrate(self) -> T;
}
pub trait IntoDomain<D>: Versioned {
fn into_domain(self) -> D;
}
pub trait FromDomain<D>: Versioned + Serialize {
fn from_domain(domain: D) -> Self;
}
pub trait LatestVersioned: Sized {
type Latest: Versioned + Serialize + FromDomain<Self>;
const ENTITY_NAME: &'static str;
const SAVE: bool = false;
fn to_latest(self) -> Self::Latest {
Self::Latest::from_domain(self)
}
}
pub trait Queryable {
const ENTITY_NAME: &'static str;
}
#[cfg(feature = "async")]
#[async_trait::async_trait]
pub trait AsyncMigratesTo<T: Versioned>: Versioned + Send {
async fn migrate(self) -> Result<T, MigrationError>;
}
#[cfg(feature = "async")]
#[async_trait::async_trait]
pub trait AsyncIntoDomain<D>: Versioned + Send {
async fn into_domain(self) -> Result<D, MigrationError>;
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VersionedWrapper<T> {
pub version: String,
pub data: T,
}
impl<T> VersionedWrapper<T> {
pub fn new(version: String, data: T) -> Self {
Self { version, data }
}
}
impl<T: Versioned> VersionedWrapper<T> {
pub fn from_versioned(data: T) -> Self {
Self {
version: T::VERSION.to_string(),
data,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct TestData {
value: String,
}
impl Versioned for TestData {
const VERSION: &'static str = "1.0.0";
}
#[test]
fn test_versioned_wrapper_from_versioned() {
let data = TestData {
value: "test".to_string(),
};
let wrapper = VersionedWrapper::from_versioned(data);
assert_eq!(wrapper.version, "1.0.0");
assert_eq!(wrapper.data.value, "test");
}
#[test]
fn test_versioned_wrapper_new() {
let data = TestData {
value: "manual".to_string(),
};
let wrapper = VersionedWrapper::new("2.0.0".to_string(), data);
assert_eq!(wrapper.version, "2.0.0");
assert_eq!(wrapper.data.value, "manual");
}
#[test]
fn test_versioned_wrapper_serialization() {
let data = TestData {
value: "serialize_test".to_string(),
};
let wrapper = VersionedWrapper::from_versioned(data);
let json = serde_json::to_string(&wrapper).expect("Serialization failed");
let deserialized: VersionedWrapper<TestData> =
serde_json::from_str(&json).expect("Deserialization failed");
assert_eq!(deserialized.version, "1.0.0");
assert_eq!(deserialized.data.value, "serialize_test");
}
#[test]
fn test_versioned_wrapper_with_complex_data() {
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct ComplexData {
id: u64,
name: String,
tags: Vec<String>,
metadata: Option<String>,
}
impl Versioned for ComplexData {
const VERSION: &'static str = "3.2.1";
}
let data = ComplexData {
id: 42,
name: "complex".to_string(),
tags: vec!["tag1".to_string(), "tag2".to_string()],
metadata: Some("meta".to_string()),
};
let wrapper = VersionedWrapper::from_versioned(data);
assert_eq!(wrapper.version, "3.2.1");
assert_eq!(wrapper.data.id, 42);
assert_eq!(wrapper.data.tags.len(), 2);
}
#[test]
fn test_versioned_wrapper_clone() {
let data = TestData {
value: "clone_test".to_string(),
};
let wrapper = VersionedWrapper::from_versioned(data);
let cloned = wrapper.clone();
assert_eq!(cloned.version, wrapper.version);
assert_eq!(cloned.data.value, wrapper.data.value);
}
#[test]
fn test_versioned_wrapper_debug() {
let data = TestData {
value: "debug".to_string(),
};
let wrapper = VersionedWrapper::from_versioned(data);
let debug_str = format!("{:?}", wrapper);
assert!(debug_str.contains("1.0.0"));
assert!(debug_str.contains("debug"));
}
}