chore: add documentation for migra core crate
This commit is contained in:
parent
f913952df0
commit
7f478671b1
11 changed files with 244 additions and 19 deletions
|
@ -1,8 +1,15 @@
|
|||
[package]
|
||||
name = "migra"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
|
||||
edition = "2018"
|
||||
description = "Migra is a simple library for managing SQL in your application"
|
||||
homepage = "https://github.com/pleshevskiy/migra"
|
||||
repository = "https://github.com/pleshevskiy/migra"
|
||||
license = "MIT OR Apache-2.0"
|
||||
keywords = ["migration", "sql", "manager"]
|
||||
categories = ["accessibility", "database"]
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
|
55
migra/README.md
Normal file
55
migra/README.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Migra
|
||||
|
||||
[![CI](https://github.com/pleshevskiy/migra/actions/workflows/rust.yml/badge.svg?branch=main)](https://github.com/pleshevskiy/migra/actions/workflows/rust.yml)
|
||||
[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)
|
||||
[![Crates.io](https://img.shields.io/crates/v/migra)](https://crates.io/crates/migra)
|
||||
![Crates.io](https://img.shields.io/crates/l/migra)
|
||||
|
||||
Migra is a simple library for managing SQL in your application.
|
||||
|
||||
For example, if you have a task list application, you can update the local user database from version to version.
|
||||
|
||||
This is main crate for [migra-cli](https://crates.io/crates/migra-cli), which allows you to manege SQL for web
|
||||
servers in any program language without being bound to SQL frameworks.
|
||||
|
||||
|
||||
### Installation
|
||||
|
||||
Add `migra = { version = "1.0" }` as a dependency in `Cargo.toml`.
|
||||
|
||||
This crate has not required predefined database clients in features with similar name.
|
||||
If you want to add them, just install crate with additional features (`postgres`, `mysql`, `sqlite`).
|
||||
|
||||
`Cargo.toml` example:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "my-crate"
|
||||
version = "0.1.0"
|
||||
authors = ["Me <user@rust-lang.org>"]
|
||||
|
||||
[dependencies]
|
||||
migra = { version = "1.0", features = ["postgres"] }
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
For more information about the crate, please read doc.
|
||||
|
||||
### Supported databases
|
||||
|
||||
| Database | Feature |
|
||||
|----------|--------------|
|
||||
| Postgres | postgres |
|
||||
| MySQL | mysql |
|
||||
| Sqlite | sqlite |
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of these:
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE_APACHE) or
|
||||
https://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE_MIT) or
|
||||
https://opensource.org/licenses/MIT)
|
|
@ -1,37 +1,39 @@
|
|||
// #![deny(missing_debug_implementations)]
|
||||
// #![deny(clippy::all, clippy::pedantic)]
|
||||
// #![allow(clippy::module_name_repetitions)]
|
||||
// #![allow(clippy::missing_errors_doc)]
|
||||
|
||||
use crate::errors::MigraResult;
|
||||
use crate::managers::{ManageMigrations, ManageTransaction};
|
||||
|
||||
/// A trait that helps to open a connection to a specific database client.
|
||||
pub trait OpenDatabaseConnection
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
/// Open database connection with predefined migrations table name.
|
||||
fn new(connection_string: &str) -> MigraResult<Self> {
|
||||
Self::manual(connection_string, "migrations")
|
||||
}
|
||||
|
||||
/// Open database connection manually with additional migration table name parameter.
|
||||
fn manual(connection_string: &str, migrations_table_name: &str) -> MigraResult<Self>;
|
||||
}
|
||||
|
||||
/// All client implementations that have migration and transaction manager implementations
|
||||
/// are considered clients.
|
||||
pub trait Client: ManageMigrations + ManageTransaction {}
|
||||
|
||||
/// If you have complex application mechanics that allow users to choose which
|
||||
/// database they can use, then you will most likely need this helper for that.
|
||||
pub type AnyClient = Box<(dyn Client + 'static)>;
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
pub mod postgres;
|
||||
mod postgres;
|
||||
#[cfg(feature = "postgres")]
|
||||
pub use self::postgres::Client as PostgresClient;
|
||||
|
||||
#[cfg(feature = "mysql")]
|
||||
pub mod mysql;
|
||||
mod mysql;
|
||||
#[cfg(feature = "mysql")]
|
||||
pub use self::mysql::Client as MysqlClient;
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
pub mod sqlite;
|
||||
mod sqlite;
|
||||
#[cfg(feature = "sqlite")]
|
||||
pub use self::sqlite::Client as SqliteClient;
|
||||
|
|
|
@ -5,6 +5,9 @@ use crate::migration;
|
|||
use mysql::prelude::*;
|
||||
use mysql::{Pool, PooledConn};
|
||||
|
||||
/// Predefined `MySQL` client.
|
||||
///
|
||||
/// **Note:** Requires enabling `mysql` feature.
|
||||
#[derive(Debug)]
|
||||
pub struct Client {
|
||||
conn: PooledConn,
|
||||
|
@ -12,6 +15,7 @@ pub struct Client {
|
|||
}
|
||||
|
||||
impl Client {
|
||||
/// Provide access to the original database connection.
|
||||
#[must_use]
|
||||
pub fn conn(&self) -> &PooledConn {
|
||||
&self.conn
|
||||
|
|
|
@ -5,12 +5,16 @@ use crate::migration;
|
|||
use postgres::{Client as PostgresClient, NoTls};
|
||||
use std::fmt;
|
||||
|
||||
/// Predefined `Postgres` client.
|
||||
///
|
||||
/// **Note:** Requires enabling `postgres` feature.
|
||||
pub struct Client {
|
||||
conn: PostgresClient,
|
||||
migrations_table_name: String,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// Provide access to the original database connection.
|
||||
#[must_use]
|
||||
pub fn conn(&self) -> &PostgresClient {
|
||||
&self.conn
|
||||
|
|
|
@ -4,6 +4,9 @@ use crate::managers::{BatchExecute, ManageMigrations, ManageTransaction};
|
|||
use crate::migration;
|
||||
use rusqlite::Connection;
|
||||
|
||||
/// Predefined `Sqlite` client.
|
||||
///
|
||||
/// **Note:** Requires enabling `sqlite` feature.
|
||||
#[derive(Debug)]
|
||||
pub struct Client {
|
||||
conn: Connection,
|
||||
|
@ -11,6 +14,7 @@ pub struct Client {
|
|||
}
|
||||
|
||||
impl Client {
|
||||
/// Provide access to the original database connection.
|
||||
#[must_use]
|
||||
pub fn conn(&self) -> &Connection {
|
||||
&self.conn
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
use std::fmt;
|
||||
use std::io;
|
||||
|
||||
/// A helper type for any standard error.
|
||||
pub type StdError = Box<dyn std::error::Error + 'static + Sync + Send>;
|
||||
|
||||
/// A helper type for any result with standard error.
|
||||
pub type StdResult<T> = Result<T, StdError>;
|
||||
|
||||
/// A helper type for any result with migra error.
|
||||
pub type MigraResult<T> = Result<T, Error>;
|
||||
|
||||
/// Migra error
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Represents database errors.
|
||||
Db(DbError),
|
||||
|
||||
/// Represents standard input output errors.
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
|
@ -36,24 +45,41 @@ impl From<io::Error> for Error {
|
|||
}
|
||||
|
||||
impl Error {
|
||||
/// Creates a database error.
|
||||
#[must_use]
|
||||
pub fn db(origin: StdError, kind: DbKind) -> Self {
|
||||
Error::Db(DbError { kind, origin })
|
||||
}
|
||||
}
|
||||
|
||||
/// All kinds of errors with witch this crate works.
|
||||
#[derive(Debug)]
|
||||
pub enum DbKind {
|
||||
/// Failed to database connection.
|
||||
DatabaseConnection,
|
||||
|
||||
/// Failed to open transaction.
|
||||
OpenTransaction,
|
||||
|
||||
/// Failed to commit transaction.
|
||||
CommitTransaction,
|
||||
|
||||
/// Failed to rollback transaction.
|
||||
RollbackTransaction,
|
||||
|
||||
/// Failed to create a migrations table.
|
||||
CreateMigrationsTable,
|
||||
|
||||
/// Failed to apply SQL.
|
||||
ApplySql,
|
||||
|
||||
/// Failed to insert a migration.
|
||||
InsertMigration,
|
||||
|
||||
/// Failed to delete a migration.
|
||||
DeleteMigration,
|
||||
|
||||
/// Failed to get applied migrations.
|
||||
GetAppliedMigrations,
|
||||
}
|
||||
|
||||
|
@ -73,6 +99,7 @@ impl fmt::Display for DbKind {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents database error.
|
||||
#[derive(Debug)]
|
||||
pub struct DbError {
|
||||
kind: DbKind,
|
||||
|
@ -84,3 +111,17 @@ impl fmt::Display for DbError {
|
|||
write!(fmt, "{} - {}", &self.kind, &self.origin)
|
||||
}
|
||||
}
|
||||
|
||||
impl DbError {
|
||||
/// Returns database error kind.
|
||||
#[must_use]
|
||||
pub fn kind(&self) -> &DbKind {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
/// Returns origin database error.
|
||||
#[must_use]
|
||||
pub fn origin(&self) -> &StdError {
|
||||
&self.origin
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,19 @@ use crate::migration;
|
|||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// Checks if the directory is a migration according to the principles of the crate.
|
||||
#[must_use]
|
||||
pub fn is_migration_dir(path: &Path) -> bool {
|
||||
path.join("up.sql").exists() && path.join("down.sql").exists()
|
||||
}
|
||||
|
||||
/// Get all migration directories from path and returns as [List].
|
||||
///
|
||||
/// This utility checks if the directory is a migration. See [`is_migration_dir`] for
|
||||
/// more information.
|
||||
///
|
||||
/// [List]: migration::List
|
||||
/// [is_migration_dir]: fs::is_migration_dir
|
||||
pub fn get_all_migrations(dir_path: &Path) -> MigraResult<migration::List> {
|
||||
let mut entries = match dir_path.read_dir() {
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => vec![],
|
||||
|
@ -24,11 +32,3 @@ pub fn get_all_migrations(dir_path: &Path) -> MigraResult<migration::List> {
|
|||
entries.sort();
|
||||
Ok(migration::List::from(entries))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn filter_pending_migrations(
|
||||
all_migrations: &migration::List,
|
||||
applied_migrations: &migration::List,
|
||||
) -> migration::List {
|
||||
all_migrations.exclude(applied_migrations)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,61 @@
|
|||
//! # Migra
|
||||
//!
|
||||
//! Migra is a simple library for managing SQL in your application.
|
||||
//!
|
||||
//! For example, if you have a task list application, you can update the local user database from version to version.
|
||||
//!
|
||||
//! This is main crate for [migra-cli](https://crates.io/crates/migra-cli), which allows you to manege SQL for web
|
||||
//! servers in any program language without being bound to SQL frameworks.
|
||||
//!
|
||||
//! ## Installation
|
||||
//!
|
||||
//! Add `migra = { version = "1.0" }` as a dependency in `Cargo.toml`.
|
||||
//!
|
||||
//! This crate has not required predefined database clients in features with similar name.
|
||||
//! If you want to add them, just install crate with additional features (`postgres`, `mysql`, `sqlite`).
|
||||
//!
|
||||
//! `Cargo.toml` example:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [package]
|
||||
//! name = "my-crate"
|
||||
//! version = "0.1.0"
|
||||
//! authors = ["Me <user@rust-lang.org>"]
|
||||
//!
|
||||
//! [dependencies]
|
||||
//! migra = { version = "1.0", features = ["postgres"] }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Supported databases
|
||||
//!
|
||||
//! | Database Client | Feature |
|
||||
//! |-----------------|--------------|
|
||||
//! | `Postgres` | postgres |
|
||||
//! | `MySQL` | mysql |
|
||||
//! | `Sqlite` | sqlite |
|
||||
//!
|
||||
#![deny(missing_debug_implementations)]
|
||||
#![warn(missing_docs)]
|
||||
#![deny(clippy::all, clippy::pedantic)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
|
||||
/// Includes additional client tools and contains predefined
|
||||
/// database clients that have been enabled in the features.
|
||||
pub mod clients;
|
||||
|
||||
mod errors;
|
||||
/// Includes all types of errors that uses in the crate.
|
||||
pub mod errors;
|
||||
|
||||
/// Includes utilities that use the file system to work.
|
||||
pub mod fs;
|
||||
|
||||
/// Includes all the basic traits that will allow you
|
||||
/// to create your own client.
|
||||
pub mod managers;
|
||||
|
||||
/// Includes basic structures of migration and migration
|
||||
/// lists, that are used in managers and fs utils.
|
||||
pub mod migration;
|
||||
|
||||
pub use errors::{Error, MigraResult as Result, StdResult};
|
||||
pub use migration::Migration;
|
||||
pub use migration::{List as MigrationList, Migration};
|
||||
|
|
|
@ -1,47 +1,71 @@
|
|||
use crate::errors::{DbKind, Error, MigraResult, StdResult};
|
||||
use crate::migration;
|
||||
|
||||
/// Used to execute SQL.
|
||||
///
|
||||
/// Is a super trait for managers.
|
||||
pub trait BatchExecute {
|
||||
/// Executes sql via original database client
|
||||
fn batch_execute(&mut self, sql: &str) -> StdResult<()>;
|
||||
}
|
||||
|
||||
/// Used to manage transaction in the database connection.
|
||||
pub trait ManageTransaction: BatchExecute {
|
||||
/// Opens transaction in database connection.
|
||||
fn begin_transaction(&mut self) -> MigraResult<()> {
|
||||
self.batch_execute("BEGIN")
|
||||
.map_err(|err| Error::db(err, DbKind::OpenTransaction))
|
||||
}
|
||||
|
||||
/// Cancels (Rollbacks) transaction in database connection.
|
||||
fn rollback_transaction(&mut self) -> MigraResult<()> {
|
||||
self.batch_execute("ROLLBACK")
|
||||
.map_err(|err| Error::db(err, DbKind::RollbackTransaction))
|
||||
}
|
||||
|
||||
/// Apply (Commit) transaction in database connection.
|
||||
fn commit_transaction(&mut self) -> MigraResult<()> {
|
||||
self.batch_execute("COMMIT")
|
||||
.map_err(|err| Error::db(err, DbKind::CommitTransaction))
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to manage migrations in the database connection.
|
||||
pub trait ManageMigrations: BatchExecute {
|
||||
/// Applies SQL. Similar to [`BatchExecute`], but returns migra [Error].
|
||||
///
|
||||
/// [BatchExecute]: managers::BatchExecute
|
||||
fn apply_sql(&mut self, sql: &str) -> MigraResult<()> {
|
||||
self.batch_execute(sql)
|
||||
.map_err(|err| Error::db(err, DbKind::ApplySql))
|
||||
}
|
||||
|
||||
/// Creates migration table.
|
||||
fn create_migrations_table(&mut self) -> MigraResult<()>;
|
||||
|
||||
/// Inserts new migration to table.
|
||||
fn insert_migration(&mut self, name: &str) -> MigraResult<u64>;
|
||||
|
||||
/// Deletes migration from table.
|
||||
fn delete_migration(&mut self, name: &str) -> MigraResult<u64>;
|
||||
|
||||
/// Get applied migrations from table.
|
||||
fn get_applied_migrations(&mut self) -> MigraResult<migration::List>;
|
||||
|
||||
/// Applies SQL to upgrade database schema and inserts new migration to table.
|
||||
///
|
||||
/// **Note:** Must be run in a transaction otherwise if the migration causes any
|
||||
/// error the data in the database may be inconsistent.
|
||||
fn run_upgrade_migration(&mut self, name: &str, content: &str) -> MigraResult<()> {
|
||||
self.apply_sql(content)?;
|
||||
self.insert_migration(name)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Applies SQL to downgrade database schema and deletes migration from table.
|
||||
///
|
||||
/// **Note:** Must be run in a transaction otherwise if the migration causes any
|
||||
/// error the data in the database may be inconsistent.
|
||||
fn run_downgrade_migration(&mut self, name: &str, content: &str) -> MigraResult<()> {
|
||||
self.apply_sql(content)?;
|
||||
self.delete_migration(name)?;
|
||||
|
|
|
@ -2,12 +2,14 @@ use crate::errors::MigraResult;
|
|||
use crate::managers::ManageMigrations;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
/// A simple wrap over string.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct Migration {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Migration {
|
||||
/// Creates new migration by name.
|
||||
#[must_use]
|
||||
pub fn new(name: &str) -> Self {
|
||||
Migration {
|
||||
|
@ -15,12 +17,21 @@ impl Migration {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns name of migration.
|
||||
#[must_use]
|
||||
pub fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap over migration vector. Can be implicitly converted to a vector and has
|
||||
/// a few of additional utilities for handling migrations.
|
||||
///
|
||||
/// Can be presented as a list of all migrations, a list of pending migrations
|
||||
/// or a list of applied migrations, depending on the implementation.
|
||||
///
|
||||
///
|
||||
///
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct List {
|
||||
inner: Vec<Migration>,
|
||||
|
@ -82,19 +93,38 @@ impl std::ops::Deref for List {
|
|||
}
|
||||
|
||||
impl List {
|
||||
/// Creates empty migration list.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
List { inner: Vec::new() }
|
||||
}
|
||||
|
||||
/// Push migration to list.
|
||||
pub fn push(&mut self, migration: Migration) {
|
||||
self.inner.push(migration)
|
||||
}
|
||||
|
||||
/// Push migration name to list.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # let mut list = List::new();
|
||||
/// list.push_name("name");
|
||||
/// # assert_eq!(list, List::from(vec!["name"]));
|
||||
/// ```
|
||||
///
|
||||
/// Is identical to the following
|
||||
/// ```rust
|
||||
/// # let mut list = List::new();
|
||||
/// list.push(Migration::new("name"));
|
||||
/// # assert_eq!(list, List::from(vec!["name"]));
|
||||
/// ```
|
||||
pub fn push_name(&mut self, name: &str) {
|
||||
self.inner.push(Migration::new(name))
|
||||
}
|
||||
|
||||
/// Check if list contains specific migration.
|
||||
#[must_use]
|
||||
pub fn contains(&self, other_migration: &Migration) -> bool {
|
||||
self.inner
|
||||
|
@ -102,11 +132,13 @@ impl List {
|
|||
.any(|migration| migration == other_migration)
|
||||
}
|
||||
|
||||
/// Check if list contains migration with specific name.
|
||||
#[must_use]
|
||||
pub fn contains_name(&self, name: &str) -> bool {
|
||||
self.inner.iter().any(|migration| migration.name() == name)
|
||||
}
|
||||
|
||||
/// Exclude specific list from current list.
|
||||
#[must_use]
|
||||
pub fn exclude(&self, list: &List) -> List {
|
||||
self.inner
|
||||
|
@ -115,6 +147,8 @@ impl List {
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// Runs a upgrade migration with SQL content and adds a new migration to the current list
|
||||
/// If there is no migration migration with specific name in the list.
|
||||
pub fn should_run_upgrade_migration(
|
||||
&mut self,
|
||||
client: &mut dyn ManageMigrations,
|
||||
|
@ -131,6 +165,8 @@ impl List {
|
|||
Ok(is_missed)
|
||||
}
|
||||
|
||||
/// Runs a downgrade migration with SQL content and removes the last migration from the
|
||||
/// current list if the last item in the list has the specified name.
|
||||
pub fn should_run_downgrade_migration(
|
||||
&mut self,
|
||||
client: &mut dyn ManageMigrations,
|
||||
|
|
Reference in a new issue