reshape/migrations/
mod.rs

1use crate::{
2    db::{Conn, Transaction},
3    schema::Schema,
4};
5use core::fmt::Debug;
6use serde::{Deserialize, Serialize};
7
8// Re-export migration types
9mod common;
10pub use common::Column;
11
12mod create_table;
13pub use create_table::CreateTable;
14
15mod alter_column;
16pub use alter_column::{AlterColumn, ColumnChanges};
17
18mod add_column;
19pub use add_column::AddColumn;
20
21mod remove_column;
22pub use remove_column::RemoveColumn;
23
24mod add_index;
25pub use add_index::{AddIndex, Index};
26
27mod remove_index;
28pub use remove_index::RemoveIndex;
29
30mod remove_table;
31pub use remove_table::RemoveTable;
32
33mod rename_table;
34pub use rename_table::RenameTable;
35
36mod create_enum;
37pub use create_enum::CreateEnum;
38
39mod remove_enum;
40pub use remove_enum::RemoveEnum;
41
42mod custom;
43pub use custom::Custom;
44
45mod add_foreign_key;
46pub use add_foreign_key::AddForeignKey;
47
48mod remove_foreign_key;
49pub use remove_foreign_key::RemoveForeignKey;
50
51#[derive(Serialize, Deserialize, Debug)]
52pub struct Migration {
53    pub name: String,
54    pub description: Option<String>,
55    pub actions: Vec<Box<dyn Action>>,
56}
57
58impl Migration {
59    pub fn new(name: impl Into<String>, description: Option<String>) -> Migration {
60        Migration {
61            name: name.into(),
62            description,
63            actions: vec![],
64        }
65    }
66
67    pub fn with_action(mut self, action: impl Action + 'static) -> Self {
68        self.actions.push(Box::new(action));
69        self
70    }
71}
72
73impl PartialEq for Migration {
74    fn eq(&self, other: &Self) -> bool {
75        self.name == other.name
76    }
77}
78
79impl Eq for Migration {}
80
81impl Clone for Migration {
82    fn clone(&self) -> Self {
83        let serialized = serde_json::to_string(self).unwrap();
84        serde_json::from_str(&serialized).unwrap()
85    }
86}
87
88pub struct MigrationContext {
89    migration_index: usize,
90    action_index: usize,
91    existing_schema_name: Option<String>,
92}
93
94impl MigrationContext {
95    pub fn new(
96        migration_index: usize,
97        action_index: usize,
98        existing_schema_name: Option<String>,
99    ) -> Self {
100        MigrationContext {
101            migration_index,
102            action_index,
103            existing_schema_name,
104        }
105    }
106
107    fn prefix(&self) -> String {
108        format!(
109            "__reshape_{:0>4}_{:0>4}",
110            self.migration_index, self.action_index
111        )
112    }
113
114    fn prefix_inverse(&self) -> String {
115        format!(
116            "__reshape_{:0>4}_{:0>4}",
117            1000 - self.migration_index,
118            1000 - self.action_index
119        )
120    }
121}
122
123#[typetag::serde(tag = "type")]
124pub trait Action: Debug {
125    fn describe(&self) -> String;
126    fn run(&self, ctx: &MigrationContext, db: &mut dyn Conn, schema: &Schema)
127        -> anyhow::Result<()>;
128    fn complete<'a>(
129        &self,
130        ctx: &MigrationContext,
131        db: &'a mut dyn Conn,
132    ) -> anyhow::Result<Option<Transaction<'a>>>;
133    fn update_schema(&self, ctx: &MigrationContext, schema: &mut Schema);
134    fn abort(&self, ctx: &MigrationContext, db: &mut dyn Conn) -> anyhow::Result<()>;
135}