Skip to content

Commit

Permalink
docs: improve docs of migration and operation
Browse files Browse the repository at this point in the history
Signed-off-by: Saurav Sharma <[email protected]>
  • Loading branch information
iamsauravsharma committed Aug 19, 2024
1 parent 1abb204 commit 17cd78c
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 65 deletions.
90 changes: 62 additions & 28 deletions src/migration.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
//! Module defining migration trait
//! Module for defining the `Migration` trait, which represents a database
//! migration.
//!
//! This module provides the necessary abstractions for defining migrations
#![cfg_attr(
feature = "sqlite",
doc = r##"
Expand Down Expand Up @@ -41,6 +44,10 @@ impl Migration<Sqlite> for ExampleMigration {
fn is_atomic(&self) -> bool {
true
}
fn is_virtual(&self) -> bool {
false
}
}
```
"##
Expand All @@ -50,52 +57,78 @@ use std::hash::Hash;

use crate::operation::Operation;

/// Trait for migration
/// Trait for defining database migration
///
/// A migration represents a set of operations that can be applied to or
/// reverted from a database. Each migration has an associated application name,
/// migration name, and may depend on other migrations.
///
/// Migrations can also replace existing migrations, enforce ordering with
/// run before and parents, and control atomicity and virtualization.
#[allow(clippy::module_name_repetitions)]
pub trait Migration<DB, State = ()>: Send + Sync {
/// Migration app name. Can be name of folder or library where migration is
/// located
/// Returns the application name associated with the migration.
/// This can be the name of the folder or library where the migration is
/// located.
fn app(&self) -> &str;

/// Migration name. Can be file name without extension
/// Returns the migration name, typically the file name without the
/// extension.
fn name(&self) -> &str;

/// Parents of migration (migrations that should be applied before this
/// migration)
/// Returns the list of parent migrations.
///
/// Parent migrations must be applied before this migration can be applied.
/// If no parent migrations are required, return an empty vector.
fn parents(&self) -> Vec<Box<dyn Migration<DB, State>>>;

/// Operation performed for migration (create, drop, etc.). Migration can
/// contains multiple operation within each other which are interrelated
/// Returns the operations associated with this migration.
///
/// A migration can include multiple operations (e.g., create, drop) that
/// are related.
fn operations(&self) -> Vec<Box<dyn Operation<DB, State>>>;

/// Replace certain migrations. If any one of listed migration is applied
/// than migration will not be applied else migration will apply/revert
/// instead of applying/reverting those migration.
/// Returns the list of migrations that this migration replaces.
///
/// If any of these migrations have been applied, this migration will not be
/// applied. Instead, it will either be applied or reverted in place of
/// those migrations.
///
/// The default implementation returns an empty vector.
fn replaces(&self) -> Vec<Box<dyn Migration<DB, State>>> {
vec![]
}

/// Run before(for applying)/after(for reverting) certain migration. This
/// can be helpful in condition where other library migration need to be
/// applied after this migration or reverted before this migration
/// Returns the list of migrations that this migration must run before(when
/// applying) or after (when reverting).
///
/// This can be useful when a migration from another library needs to be
/// applied after this migration or reverted before this migration.
///
/// The default implementation returns an empty vector.
fn run_before(&self) -> Vec<Box<dyn Migration<DB, State>>> {
vec![]
}

/// Whether migration is atomic or not. By default it is atomic so this
/// function returns `true`. If you make migration non atomic all its
/// operation will be non atomic if any operation needs to be atomic it is
/// recommended to split migration so one migration will have atomic
/// operation where as another have non atomic operation
/// Indicates whether the migration is atomic.
/// By default, this function returns `true`, meaning the migration is
/// atomic.
///
/// If the migration is non-atomic, all its operations will be non-atomic as
/// well. For migrations requiring mixed atomicity, it's recommended to
/// split them into separate migrations, each handling atomic and
/// non-atomic operations respectively.
fn is_atomic(&self) -> bool {
true
}

/// Whether migration is virtual or not. By default migration are not
/// virtual. If migration is virtual than it expects another migration with
/// same app and name present inside migration list which is not virtual.
/// For virtual migration all other methods gets ignored since it is
/// reference to actual migration instead of actual migration
/// Indicates whether the migration is virtual.
/// By default, this function returns `false`, meaning the migration is not
/// virtual.
///
/// A virtual migration serves as a reference to another migration with the
/// same app and name. If the migration is virtual, all other methods
/// are ignored.
fn is_virtual(&self) -> bool {
false
}
Expand Down Expand Up @@ -142,9 +175,10 @@ where
}
}

/// Migration struct created from sql table. Struct contains 4 fields which maps
/// to `id`, `app`, `name`, `applied_time` sql fields. It is used to list
/// applied migrations
/// Struct representing a migration row from the database.
///
/// This struct corresponds to the id, app, name, and applied time fields in the
/// database. It is used to list the migrations that have been applied.
#[derive(sqlx::FromRow, Clone)]
pub struct AppliedMigrationSqlRow {
id: i32,
Expand Down
70 changes: 42 additions & 28 deletions src/migrator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,10 @@ enum PlanType {
Apply,
Revert,
}

/// Struct which determine type of plan to use
/// Struct that determines the type of migration plan to execute.
///
/// A `Plan` can specify whether to apply or revert migrations, and may target
/// all migrations, specific migrations, or a limited number of migrations.
#[derive(Debug)]
pub struct Plan {
plan_type: PlanType,
Expand All @@ -200,25 +202,25 @@ impl Plan {
}
}

/// Create new plan for apply all
/// Creates a new plan to apply all migrations.
#[must_use]
pub fn apply_all() -> Self {
Self::new(PlanType::Apply, None, None)
}

/// Create new plan for apply for provided app and migration name
/// Creates a new plan to apply a specific migration by name.
#[must_use]
pub fn apply_name(app: &str, name: &Option<String>) -> Self {
Self::new(PlanType::Apply, Some((app.to_string(), name.clone())), None)
}

/// Create new plan for apply count
/// Creates a new plan to apply a limited number of migrations.
#[must_use]
pub fn apply_count(count: usize) -> Self {
Self::new(PlanType::Apply, None, Some(count))
}

/// Create new plan for revert all
/// Creates a new plan to revert all migrations.
#[must_use]
pub fn revert_all() -> Self {
Self::new(PlanType::Revert, None, None)
Expand All @@ -234,33 +236,37 @@ impl Plan {
)
}

/// Create new plan for revert count
/// Creates a new plan to revert a limited number of migrations.
#[must_use]
pub fn revert_count(count: usize) -> Self {
Self::new(PlanType::Revert, None, Some(count))
}
}

/// Info trait which implements some of database agnostic methods to add
/// migration or returns immutable or mutable migrations list
/// The `Info` trait provides database-agnostic methods for managing migrations
/// and interacting with migration states.
pub trait Info<DB, State> {
/// Return state used in migrator
/// Returns the current state used by the migrator.
fn state(&self) -> &State;

/// Return migrations
/// Returns a reference to the list of migrations.
fn migrations(&self) -> &Vec<BoxMigration<DB, State>>;

/// Return mutable reference of migrations
/// Returns a mutable reference to the list of migrations.
fn migrations_mut(&mut self) -> &mut Vec<BoxMigration<DB, State>>;

/// Add vector of migrations to Migrator object
/// Adds a list of migrations to the migrator.
///
/// This method accepts a vector of migrations and adds each one
/// individually to ensure proper handling of migration relationships
/// and duplicates.
fn add_migrations(&mut self, migrations: Vec<BoxMigration<DB, State>>) {
for migration in migrations {
self.add_migration(migration);
}
}

/// Add single migration to migrator object
/// Adds a single migration to the migrator.
fn add_migration(&mut self, migration: BoxMigration<DB, State>) {
// if virtual migration is present in list with same app and name than remove
// virtual migration from list
Expand Down Expand Up @@ -301,9 +307,13 @@ pub trait Info<DB, State> {
}
}

/// Trait which is implemented for database for performing different
/// operations/action on database. Usually this trait is implemented for
/// database to support database along with info trait
/// The `DatabaseOperation` trait defines a set of methods for performing
/// operations related to migration management on the database.
///
/// This trait is typically implemented for a database to support migration
/// operations, such as ensuring the migration table exists, adding or
/// removing migrations from the table, and locking the database during
/// migration processes.
#[async_trait::async_trait]
pub trait DatabaseOperation<DB, State>
where
Expand All @@ -322,23 +332,24 @@ where
connection: &mut <DB as sqlx::Database>::Connection,
) -> Result<(), Error>;

/// Add migration to migration db table
/// Adds a migration record to the migration table in the database.
#[allow(clippy::borrowed_box)]
async fn add_migration_to_db_table(
&self,
connection: &mut <DB as sqlx::Database>::Connection,
migration: &BoxMigration<DB, State>,
) -> Result<(), Error>;

/// Delete migration from migration db table
/// Removes a migration record from the migration table in the database.
#[allow(clippy::borrowed_box)]
async fn delete_migration_from_db_table(
&self,
connection: &mut <DB as sqlx::Database>::Connection,
migration: &BoxMigration<DB, State>,
) -> Result<(), Error>;

/// List all applied migrations from database as struct
/// Fetches the list of applied migrations from the migration table in the
/// database.
async fn fetch_applied_migration_from_db(
&self,
connection: &mut <DB as sqlx::Database>::Connection,
Expand Down Expand Up @@ -537,20 +548,23 @@ fn get_recursive<'get, DB, State>(
}
recursive_vec
}

/// Migrate trait which migrate a database according to requirements. This trait
/// implements all methods which depends on `DatabaseOperation` trait and `Info`
/// trait. This trait doesn't requires to implement any method since all
/// function have default implementation and all methods are database agnostics
/// The `Migrate` trait defines methods to manage and apply database migrations
/// according to a given plan.
///
/// This trait combines the functionalities of the `Info` and
/// `DatabaseOperation` traits, providing a full set of migration capabilities.
/// All methods have default implementations, meaning no explicit implementation
/// is required. Additionally, all methods are database-agnostic.
#[async_trait::async_trait]
pub trait Migrate<DB, State>: Info<DB, State> + DatabaseOperation<DB, State> + Send + Sync
where
DB: sqlx::Database,
State: Send + Sync,
{
/// Generate migration plan according to plan. Returns a vector of
/// migration. If plan is none than it will generate plan with all
/// migrations in chronological order of apply
/// Generate migration plan according to plan.
///
/// Returns a vector of migration. If plan is none than it will generate
/// plan with all migrations in order of apply
#[allow(clippy::too_many_lines)]
async fn generate_migration_plan(
&self,
Expand Down
41 changes: 32 additions & 9 deletions src/operation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
//! Operation module
//! Module for defining the `Operation` trait
//!
//! This module provides the `Operation` trait, allowing users to define
//! database operations that can be executed as part of a migration process.
//! These operations can be applied (`up`) or optionally reverted (`down`).
#![cfg_attr(
feature = "sqlite",
doc = r##"
Expand Down Expand Up @@ -40,23 +44,39 @@ impl Operation<Sqlite> for ExampleOperation {

use crate::error::Error;

/// Trait for operation
/// Trait for defining a database operation.
///
/// An Operation represents action that can be applied to or reverted from a
/// database during a migration. Each operation can have an up method for
/// applying the change and an optional down method for rolling it back.
///
/// Operations can also specify whether they are "destructible," meaning that
/// they require user confirmation before being applied, due to potential data
/// loss or irreversible changes
#[allow(clippy::module_name_repetitions)]
#[async_trait::async_trait]
pub trait Operation<DB, State = ()>: Send + Sync
where
DB: sqlx::Database,
State: Send + Sync,
{
/// Up command to be executed during migration apply
/// The up method executes the operation when applying the migration.
///
/// This method is called when the migration is being applied to the
/// database. Implement this method to define the changes you want to
/// apply.
async fn up(
&self,
connection: &mut <DB as sqlx::Database>::Connection,
state: &State,
) -> Result<(), Error>;

/// Down command to be executed during migration rollback. If it is not
/// implemented than operation is irreversible operation.
/// The `down` method reverses the operation when rolling back the
/// migration.
///
/// This method is called when the migration is being rolled back. Implement
/// this method if you want to make the operation reversible. If not
/// implemented, the operation is considered irreversible.
async fn down(
&self,
connection: &mut <DB as sqlx::Database>::Connection,
Expand All @@ -67,10 +87,13 @@ where
return Err(Error::IrreversibleOperation);
}

/// Whether up operation is destructible or not. If operation is
/// destructible than user should answer before running migration through
/// cli. By default up operation are false. Down operation are always
/// destructible and cannot be changed
/// Indicates whether the `up` operation is destructible.
///
/// If the operation is destructible, the user will be prompted for
/// confirmation before running the migration via the CLI, due to the
/// potential for data loss or irreversible changes. By default, `up`
/// operations are considered non-destructible. Note that `down` operations
/// are always considered destructible and cannot be changed.
fn is_destructible(&self) -> bool {
false
}
Expand Down

0 comments on commit 17cd78c

Please sign in to comment.