mirror of
https://github.com/fabianlindfors/reshape.git
synced 2024-11-22 03:53:38 +03:00
Switch State
struct into enum
With the old fields in the `State` broken out we can now make `State` a proper enum, replacing the nested `Status`. The naming with both `State` and `Status` was a bit confusing.
This commit is contained in:
parent
b1b79e26e5
commit
2d04d33b95
39
src/lib.rs
39
src/lib.rs
@ -15,7 +15,7 @@ pub mod migrations;
|
||||
mod schema;
|
||||
mod state;
|
||||
|
||||
pub use crate::state::{State, Status};
|
||||
pub use crate::state::State;
|
||||
|
||||
pub struct Reshape {
|
||||
pub state: State,
|
||||
@ -46,7 +46,7 @@ impl Reshape {
|
||||
|
||||
fn new_with_config(config: &Config) -> anyhow::Result<Reshape> {
|
||||
let mut db = DbConn::connect(config)?;
|
||||
let state = State::load(&mut db);
|
||||
let state = State::load(&mut db)?;
|
||||
|
||||
Ok(Reshape { db, state })
|
||||
}
|
||||
@ -55,15 +55,15 @@ impl Reshape {
|
||||
where
|
||||
T: IntoIterator<Item = Migration>,
|
||||
{
|
||||
self.state = State::load(&mut self.db);
|
||||
self.state = State::load(&mut self.db)?;
|
||||
|
||||
// Make sure no migration is in progress
|
||||
if let state::Status::InProgress { .. } = &self.state.status {
|
||||
if let State::InProgress { .. } = &self.state {
|
||||
println!("Migration already in progress, please complete using 'reshape complete'");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let state::Status::Completing { .. } = &self.state.status {
|
||||
if let State::Completing { .. } = &self.state {
|
||||
println!(
|
||||
"Migration already in progress and has started completion, please finish using 'reshape complete'"
|
||||
);
|
||||
@ -83,9 +83,9 @@ impl Reshape {
|
||||
|
||||
// If we have already started applying some migrations we need to ensure that
|
||||
// they are the same ones we want to apply now
|
||||
if let state::Status::Applying {
|
||||
if let State::Applying {
|
||||
migrations: existing_migrations,
|
||||
} = &self.state.status
|
||||
} = &self.state
|
||||
{
|
||||
if existing_migrations != &remaining_migrations {
|
||||
return Err(anyhow!(
|
||||
@ -198,8 +198,8 @@ impl Reshape {
|
||||
|
||||
pub fn complete_migration(&mut self) -> anyhow::Result<()> {
|
||||
// Make sure a migration is in progress
|
||||
let (remaining_migrations, starting_migration_index, starting_action_index) = match self.state.status.clone() {
|
||||
state::Status::InProgress { migrations } => {
|
||||
let (remaining_migrations, starting_migration_index, starting_action_index) = match self.state.clone() {
|
||||
State::InProgress { migrations } => {
|
||||
// Move into the Completing state. Once in this state,
|
||||
// the migration can't be aborted and must be completed.
|
||||
self.state.completing(migrations.clone(), 0, 0);
|
||||
@ -207,18 +207,18 @@ impl Reshape {
|
||||
|
||||
(migrations, 0, 0)
|
||||
},
|
||||
state::Status::Completing {
|
||||
State::Completing {
|
||||
migrations,
|
||||
current_migration_index,
|
||||
current_action_index
|
||||
} => (migrations, current_migration_index, current_action_index),
|
||||
state::Status::Aborting { .. } => {
|
||||
State::Aborting { .. } => {
|
||||
return Err(anyhow!("migration been aborted and can't be completed. Please finish using `reshape abort`."))
|
||||
}
|
||||
state::Status::Applying { .. } => {
|
||||
State::Applying { .. } => {
|
||||
return Err(anyhow!("a previous migration unexpectedly failed. Please run `reshape migrate` to try applying the migration again."))
|
||||
}
|
||||
state::Status::Idle => {
|
||||
State::Idle => {
|
||||
println!("No migration in progress");
|
||||
return Ok(());
|
||||
}
|
||||
@ -396,7 +396,7 @@ impl Reshape {
|
||||
))?;
|
||||
}
|
||||
|
||||
if let Status::InProgress { migrations } = &self.state.status {
|
||||
if let State::InProgress { migrations } = &self.state {
|
||||
let target_migration = migrations.last().unwrap().name.to_string();
|
||||
self.db.run(&format!(
|
||||
"DROP SCHEMA IF EXISTS {} CASCADE",
|
||||
@ -426,10 +426,9 @@ impl Reshape {
|
||||
pub fn abort(&mut self) -> anyhow::Result<()> {
|
||||
let (remaining_migrations, last_migration_index, last_action_index) = match self
|
||||
.state
|
||||
.status
|
||||
.clone()
|
||||
{
|
||||
Status::InProgress { migrations } | Status::Applying { migrations } => {
|
||||
State::InProgress { migrations } | State::Applying { migrations } => {
|
||||
// Set to the Aborting state. Once this is done, the migration has to
|
||||
// be fully aborted and can't be completed.
|
||||
self.state.aborting(migrations.clone(), 0, 0);
|
||||
@ -437,15 +436,15 @@ impl Reshape {
|
||||
|
||||
(migrations, usize::MAX, usize::MAX)
|
||||
}
|
||||
Status::Aborting {
|
||||
State::Aborting {
|
||||
migrations,
|
||||
last_migration_index,
|
||||
last_action_index,
|
||||
} => (migrations, last_migration_index, last_action_index),
|
||||
Status::Completing { .. } => {
|
||||
State::Completing { .. } => {
|
||||
return Err(anyhow!("Migration completion has already been started. Please run `reshape complete` again to finish it."));
|
||||
}
|
||||
Status::Idle => {
|
||||
State::Idle => {
|
||||
println!("No migration is in progress");
|
||||
return Ok(());
|
||||
}
|
||||
@ -467,7 +466,7 @@ impl Reshape {
|
||||
|
||||
helpers::tear_down_helpers(&mut self.db).context("failed to tear down helpers")?;
|
||||
|
||||
self.state.status = state::Status::Idle;
|
||||
self.state = State::Idle;
|
||||
self.state
|
||||
.save(&mut self.db)
|
||||
.context("failed to save state")?;
|
||||
|
71
src/state.rs
71
src/state.rs
@ -4,14 +4,9 @@ use anyhow::anyhow;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use version::version;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct State {
|
||||
pub status: Status,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(tag = "status")]
|
||||
pub enum Status {
|
||||
#[serde(tag = "state")]
|
||||
pub enum State {
|
||||
#[serde(rename = "idle")]
|
||||
Idle,
|
||||
|
||||
@ -37,24 +32,23 @@ pub enum Status {
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn load(db: &mut impl Conn) -> State {
|
||||
Self::ensure_schema_and_table(db);
|
||||
pub fn load(db: &mut impl Conn) -> anyhow::Result<State> {
|
||||
Self::ensure_schema_and_table(db)?;
|
||||
|
||||
let results = db
|
||||
.query("SELECT value FROM reshape.data WHERE key = 'state'")
|
||||
.unwrap();
|
||||
let results = db.query("SELECT value FROM reshape.data WHERE key = 'state'")?;
|
||||
|
||||
match results.first() {
|
||||
let state = match results.first() {
|
||||
Some(row) => {
|
||||
let json: serde_json::Value = row.get(0);
|
||||
serde_json::from_value(json).unwrap()
|
||||
serde_json::from_value(json)?
|
||||
}
|
||||
None => Default::default(),
|
||||
}
|
||||
};
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub fn save(&self, db: &mut impl Conn) -> anyhow::Result<()> {
|
||||
Self::ensure_schema_and_table(db);
|
||||
Self::ensure_schema_and_table(db)?;
|
||||
|
||||
let json = serde_json::to_value(self)?;
|
||||
db.query_with_params(
|
||||
@ -67,17 +61,17 @@ impl State {
|
||||
pub fn clear(&mut self, db: &mut impl Conn) -> anyhow::Result<()> {
|
||||
db.run("DROP SCHEMA reshape CASCADE")?;
|
||||
|
||||
let default = Self::default();
|
||||
self.status = default.status;
|
||||
*self = Self::default();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Complete will change the status from Completing to Idle
|
||||
// Complete will change the state from Completing to Idle
|
||||
pub fn complete(&mut self, db: &mut impl Conn) -> anyhow::Result<()> {
|
||||
let current_status = std::mem::replace(&mut self.status, Status::Idle);
|
||||
match current_status {
|
||||
Status::Completing { migrations, .. } => {
|
||||
let current_state = std::mem::replace(self, Self::Idle);
|
||||
|
||||
match current_state {
|
||||
Self::Completing { migrations, .. } => {
|
||||
// Add migrations and update state in a transaction to ensure atomicity
|
||||
let mut transaction = db.transaction()?;
|
||||
save_migrations(&mut transaction, &migrations)?;
|
||||
@ -85,8 +79,8 @@ impl State {
|
||||
transaction.commit()?;
|
||||
}
|
||||
_ => {
|
||||
// Move old status back
|
||||
self.status = current_status;
|
||||
// Move old state back
|
||||
*self = current_state;
|
||||
|
||||
return Err(anyhow!(
|
||||
"couldn't update state to be completed, not in Completing state"
|
||||
@ -98,13 +92,13 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn applying(&mut self, new_migrations: Vec<Migration>) {
|
||||
self.status = Status::Applying {
|
||||
*self = Self::Applying {
|
||||
migrations: new_migrations,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn in_progress(&mut self, new_migrations: Vec<Migration>) {
|
||||
self.status = Status::InProgress {
|
||||
*self = Self::InProgress {
|
||||
migrations: new_migrations,
|
||||
};
|
||||
}
|
||||
@ -115,7 +109,7 @@ impl State {
|
||||
current_migration_index: usize,
|
||||
current_action_index: usize,
|
||||
) {
|
||||
self.status = Status::Completing {
|
||||
*self = Self::Completing {
|
||||
migrations,
|
||||
current_migration_index,
|
||||
current_action_index,
|
||||
@ -128,20 +122,19 @@ impl State {
|
||||
last_migration_index: usize,
|
||||
last_action_index: usize,
|
||||
) {
|
||||
self.status = Status::Aborting {
|
||||
*self = Self::Aborting {
|
||||
migrations,
|
||||
last_migration_index,
|
||||
last_action_index,
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_schema_and_table(db: &mut impl Conn) {
|
||||
db.run("CREATE SCHEMA IF NOT EXISTS reshape").unwrap();
|
||||
fn ensure_schema_and_table(db: &mut impl Conn) -> anyhow::Result<()> {
|
||||
db.run("CREATE SCHEMA IF NOT EXISTS reshape")?;
|
||||
|
||||
// Create data table which will be a key-value table containing
|
||||
// the version and current state.
|
||||
db.run("CREATE TABLE IF NOT EXISTS reshape.data (key TEXT PRIMARY KEY, value JSONB)")
|
||||
.unwrap();
|
||||
db.run("CREATE TABLE IF NOT EXISTS reshape.data (key TEXT PRIMARY KEY, value JSONB)")?;
|
||||
|
||||
// Create migrations table which will store all completed migrations
|
||||
db.run(
|
||||
@ -154,11 +147,10 @@ impl State {
|
||||
completed_at TIMESTAMP DEFAULT NOW()
|
||||
)
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
|
||||
// Update the current version
|
||||
let encoded_version = serde_json::to_value(version!().to_string()).unwrap();
|
||||
let encoded_version = serde_json::to_value(version!().to_string())?;
|
||||
db.query_with_params(
|
||||
"
|
||||
INSERT INTO reshape.data (key, value)
|
||||
@ -166,16 +158,15 @@ impl State {
|
||||
ON CONFLICT (key) DO UPDATE SET value = $1
|
||||
",
|
||||
&[&encoded_version],
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
State {
|
||||
status: Status::Idle,
|
||||
}
|
||||
Self::Idle
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
use reshape::migrations::{AddColumn, Column, ColumnBuilder, CreateTableBuilder, Migration};
|
||||
use reshape::Status;
|
||||
|
||||
mod common;
|
||||
|
||||
@ -59,7 +58,6 @@ fn add_column() {
|
||||
|
||||
// Run first migration, should automatically finish
|
||||
reshape.migrate(first_migrations.clone()).unwrap();
|
||||
assert!(matches!(reshape.state.status, Status::Idle));
|
||||
|
||||
// Update search paths
|
||||
old_db
|
||||
@ -151,7 +149,6 @@ fn add_column_nullable() {
|
||||
|
||||
// Run first migration, should automatically finish
|
||||
reshape.migrate(first_migrations.clone()).unwrap();
|
||||
assert!(matches!(reshape.state.status, Status::Idle));
|
||||
|
||||
// Update search paths
|
||||
old_db
|
||||
@ -246,7 +243,6 @@ fn add_column_with_default() {
|
||||
|
||||
// Run first migration, should automatically finish
|
||||
reshape.migrate(first_migrations.clone()).unwrap();
|
||||
assert!(matches!(reshape.state.status, Status::Idle));
|
||||
|
||||
// Update search paths
|
||||
old_db
|
||||
|
@ -1,7 +1,6 @@
|
||||
use reshape::migrations::{
|
||||
AddIndex, AlterColumn, ColumnBuilder, ColumnChanges, CreateTableBuilder, Migration,
|
||||
};
|
||||
use reshape::Status;
|
||||
|
||||
mod common;
|
||||
|
||||
@ -46,7 +45,6 @@ fn alter_column_data() {
|
||||
|
||||
// Run first migration, should automatically finish
|
||||
reshape.migrate(first_migrations.clone()).unwrap();
|
||||
assert!(matches!(reshape.state.status, Status::Idle));
|
||||
|
||||
// Update search paths
|
||||
old_db
|
||||
@ -146,7 +144,6 @@ fn alter_column_set_not_null() {
|
||||
|
||||
// Run first migration, should automatically finish
|
||||
reshape.migrate(first_migrations.clone()).unwrap();
|
||||
assert!(matches!(reshape.state.status, Status::Idle));
|
||||
|
||||
// Update search paths
|
||||
old_db
|
||||
@ -247,7 +244,6 @@ fn alter_column_rename() {
|
||||
|
||||
// Run first migration, should automatically finish
|
||||
reshape.migrate(first_migrations.clone()).unwrap();
|
||||
assert!(matches!(reshape.state.status, Status::Idle));
|
||||
|
||||
// Update search paths
|
||||
old_db
|
||||
@ -343,7 +339,6 @@ fn alter_column_multiple() {
|
||||
|
||||
// Run first migration, should automatically finish
|
||||
reshape.migrate(first_migrations.clone()).unwrap();
|
||||
assert!(matches!(reshape.state.status, Status::Idle));
|
||||
|
||||
// Update search paths
|
||||
old_db
|
||||
@ -455,7 +450,6 @@ fn alter_column_default() {
|
||||
|
||||
// Run first migration, should automatically finish
|
||||
reshape.migrate(first_migrations.clone()).unwrap();
|
||||
assert!(matches!(reshape.state.status, Status::Idle));
|
||||
|
||||
// Update search paths
|
||||
old_db
|
||||
@ -564,7 +558,6 @@ fn alter_column_with_index() {
|
||||
|
||||
// Run first migration, should automatically finish
|
||||
reshape.migrate(first_migrations.clone()).unwrap();
|
||||
assert!(matches!(reshape.state.status, Status::Idle));
|
||||
|
||||
// Run second migration
|
||||
reshape.migrate(second_migrations.clone()).unwrap();
|
||||
|
@ -1,7 +1,4 @@
|
||||
use reshape::{
|
||||
migrations::{ColumnBuilder, CreateTableBuilder, ForeignKey, Migration},
|
||||
Status,
|
||||
};
|
||||
use reshape::migrations::{ColumnBuilder, CreateTableBuilder, ForeignKey, Migration};
|
||||
|
||||
mod common;
|
||||
|
||||
@ -40,7 +37,6 @@ fn create_table() {
|
||||
reshape
|
||||
.migrate(vec![create_table_migration.clone()])
|
||||
.unwrap();
|
||||
assert!(matches!(reshape.state.status, Status::Idle));
|
||||
|
||||
// Ensure table was created
|
||||
let result = db
|
||||
|
@ -1,5 +1,4 @@
|
||||
use reshape::migrations::{AddColumn, Column, CreateTable, Migration};
|
||||
use reshape::Status;
|
||||
|
||||
mod common;
|
||||
|
||||
@ -45,7 +44,6 @@ fn invalid_migration() {
|
||||
|
||||
// Run first migration, should automatically finish
|
||||
reshape.migrate(first_migrations.clone()).unwrap();
|
||||
assert!(matches!(reshape.state.status, Status::Idle));
|
||||
|
||||
// Update search paths
|
||||
old_db
|
||||
|
Loading…
Reference in New Issue
Block a user