|
@@ -0,0 +1,155 @@
|
|
|
+#![allow(missing_docs)]
|
|
|
+
|
|
|
+use crate::schema::{
|
|
|
+ self, build::collect_from_database, entity::Entity, DatabaseItem, DatabaseItemVisitor, Schema,
|
|
|
+};
|
|
|
+use crate::{ConnectionLease, DBResult, Error};
|
|
|
+
|
|
|
+pub trait MigratableEntity<From: Entity>: 'static + Entity {
|
|
|
+ fn migrate(from: From) -> DBResult<Option<Self>>
|
|
|
+ where
|
|
|
+ Self: Sized;
|
|
|
+}
|
|
|
+
|
|
|
+impl<T: 'static + Entity> MigratableEntity<T> for T {
|
|
|
+ fn migrate(from: T) -> DBResult<Option<Self>>
|
|
|
+ where
|
|
|
+ Self: Sized,
|
|
|
+ {
|
|
|
+ Ok(Some(from))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub trait MigratableItem<From: DatabaseItem>: 'static + DatabaseItem {
|
|
|
+ fn run_migration(lease: &mut ConnectionLease) -> DBResult<()>;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+pub trait MigratableSchema<From: Schema>: 'static + Schema {
|
|
|
+ fn run_migration(lease: &mut ConnectionLease) -> DBResult<()> {
|
|
|
+ let i = Self::default();
|
|
|
+
|
|
|
+ struct Visitor;
|
|
|
+ impl DatabaseItemVisitor for Visitor {
|
|
|
+ fn visit_idmap<T: Entity>(&mut self)
|
|
|
+ where
|
|
|
+ Self: Sized,
|
|
|
+ {
|
|
|
+ todo!()
|
|
|
+ }
|
|
|
+ fn visit_index<T: Entity, PL: schema::entity::EntityPartList<Entity = T>>(&mut self)
|
|
|
+ where
|
|
|
+ Self: Sized,
|
|
|
+ {
|
|
|
+ todo!()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut v = Visitor;
|
|
|
+ i.accept_item_visitor(&mut v);
|
|
|
+ todo!()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// identity implementations
|
|
|
+// impl<T: 'static + Schema> MigratableSchema<T> for T { }
|
|
|
+
|
|
|
+*/
|
|
|
+
|
|
|
+#[derive(Default, microrm_macros::Schema)]
|
|
|
+pub struct EmptySchema {}
|
|
|
+
|
|
|
+impl MigratableItem<EmptySchema> for EmptySchema {
|
|
|
+ fn run_migration(_lease: &mut ConnectionLease) -> DBResult<()> { unreachable!() }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T: 'static + DatabaseItem> MigratableItem<()> for T {
|
|
|
+ fn run_migration(_lease: &mut ConnectionLease) -> DBResult<()> { unreachable!() }
|
|
|
+}
|
|
|
+
|
|
|
+impl Schema for () {
|
|
|
+ fn accept_item_visitor(&self, _visitor: &mut impl schema::DatabaseItemVisitor)
|
|
|
+ where
|
|
|
+ Self: Sized,
|
|
|
+ {
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub trait SchemaList {
|
|
|
+ type Head: Schema + MigratableItem<<Self::Tail as SchemaList>::Head>;
|
|
|
+ type Tail: SchemaList;
|
|
|
+ const EMPTY: bool = false;
|
|
|
+}
|
|
|
+
|
|
|
+impl SchemaList for () {
|
|
|
+ type Head = ();
|
|
|
+ type Tail = ();
|
|
|
+ const EMPTY: bool = true;
|
|
|
+}
|
|
|
+
|
|
|
+impl<S0: Schema> SchemaList for (S0,)
|
|
|
+where
|
|
|
+ S0: MigratableItem<()>,
|
|
|
+{
|
|
|
+ type Head = S0;
|
|
|
+ type Tail = ();
|
|
|
+}
|
|
|
+
|
|
|
+impl<S0: Schema, S1: Schema> SchemaList for (S0, S1)
|
|
|
+where
|
|
|
+ S1: MigratableItem<S0>,
|
|
|
+{
|
|
|
+ type Head = S1;
|
|
|
+ type Tail = (S0,);
|
|
|
+}
|
|
|
+
|
|
|
+impl<S0: Schema, S1: Schema, S2: Schema> SchemaList for (S0, S1, S2)
|
|
|
+where
|
|
|
+ S1: MigratableItem<S0>,
|
|
|
+ S2: MigratableItem<S1>,
|
|
|
+{
|
|
|
+ type Head = S2;
|
|
|
+ type Tail = (S0, S1);
|
|
|
+}
|
|
|
+
|
|
|
+impl<S0: Schema, S1: Schema, S2: Schema, S3: Schema> SchemaList for (S0, S1, S2, S3)
|
|
|
+where
|
|
|
+ S1: MigratableItem<S0>,
|
|
|
+ S2: MigratableItem<S1>,
|
|
|
+ S3: MigratableItem<S2>,
|
|
|
+{
|
|
|
+ type Head = S3;
|
|
|
+ type Tail = (S0, S1, S2);
|
|
|
+}
|
|
|
+
|
|
|
+fn migration_helper<A: SchemaList>(lease: &mut ConnectionLease) -> DBResult<()> {
|
|
|
+ if A::EMPTY {
|
|
|
+ return Err(Error::IncompatibleSchema);
|
|
|
+ }
|
|
|
+
|
|
|
+ let built = collect_from_database(&<A::Head>::default());
|
|
|
+ match built.check(lease) {
|
|
|
+ Some(true) => Ok(()),
|
|
|
+ Some(false) => {
|
|
|
+ migration_helper::<A::Tail>(lease)?;
|
|
|
+
|
|
|
+ // migrate from (A::Tail::Head) to A::Head
|
|
|
+ type MigrateTo<A> = <A as SchemaList>::Head;
|
|
|
+ type MigrateFrom<A> = <<A as SchemaList>::Tail as SchemaList>::Head;
|
|
|
+ <MigrateTo<A> as MigratableItem<MigrateFrom<A>>>::run_migration(lease)
|
|
|
+ },
|
|
|
+ None => Err(Error::EmptyDatabase),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn run_migration<A: SchemaList>(lease: &mut ConnectionLease) -> DBResult<()> {
|
|
|
+ // let metadb = meta::MetadataDB::default();
|
|
|
+
|
|
|
+ let _ = migration_helper::<A>(lease)?;
|
|
|
+
|
|
|
+ // easy case: see if the head is the most recent
|
|
|
+
|
|
|
+ // super::build::collect_from_database(
|
|
|
+ // metadb.metastore.get(
|
|
|
+ Ok(())
|
|
|
+}
|