Skip to main content

turso_orm_migration/
lib.rs

1//! Migration: versioned schema migrations for turso-orm.
2//!
3//! A migration is a type implementing [`MigrationTrait`] (with `up` and
4//! `down`) and [`MigrationName`] (derived with [`DeriveMigrationName`] from
5//! the module name). A [`MigratorTrait`] lists the migrations in order and
6//! applies them through a [`SchemaManager`], which wraps the transaction a
7//! migration runs in and offers DDL helpers and catalog lookups.
8//!
9//! The crate owns the bookkeeping — which migrations have been applied, in
10//! which order — and the transactional envelope around each one. It does not
11//! own DDL rendering (that is `turso_sql`, re-exported through the prelude)
12//! nor the connection (that is `turso_orm`).
13//!
14//! # Design decisions
15//!
16//! - Each migration runs together with its bookkeeping insert inside one
17//!   `BEGIN IMMEDIATE` transaction, so a failing migration leaves neither a
18//!   partial schema nor a stale version row behind.
19//! - Versions are the migration names, taken from the module path, so that
20//!   every migration struct can simply be called `Migration` and the file
21//!   name is the single source of truth for ordering.
22//! - `down` defaults to failing with [`DbErr::Migration`], so a migration
23//!   that cannot be reverted says so instead of silently doing nothing.
24//!
25//! # Example
26//!
27//! ```ignore
28//! use turso_orm_migration::prelude::*;
29//!
30//! mod m20240101_000001_create_user {
31//!     use turso_orm_migration::prelude::*;
32//!
33//!     #[derive(DeriveMigrationName)]
34//!     pub struct Migration;
35//!
36//!     #[async_trait]
37//!     impl MigrationTrait for Migration {
38//!         async fn up(&self, manager: &SchemaManager<'_>) -> Result<(), DbErr> {
39//!             manager
40//!                 .create_table(
41//!                     Table::create()
42//!                         .table("user")
43//!                         .col(ColumnDef::integer("id").primary_key().auto_increment())
44//!                         .col(ColumnDef::text("email").not_null().unique_key()),
45//!                 )
46//!                 .await
47//!         }
48//!
49//!         async fn down(&self, manager: &SchemaManager<'_>) -> Result<(), DbErr> {
50//!             manager.drop_table(Table::drop().table("user")).await
51//!         }
52//!     }
53//! }
54//!
55//! pub struct Migrator;
56//!
57//! #[async_trait]
58//! impl MigratorTrait for Migrator {
59//!     fn migrations() -> Vec<Box<dyn MigrationTrait>> {
60//!         vec![Box::new(m20240101_000001_create_user::Migration)]
61//!     }
62//! }
63//! ```
64
65#![cfg_attr(docsrs, feature(doc_cfg))]
66
67mod manager;
68mod migrator;
69
70pub use manager::SchemaManager;
71pub use migrator::{MigrationIssue, MigrationStatus, MigratorTrait};
72
73pub use async_trait::async_trait;
74pub use turso_orm;
75pub use turso_orm::DbErr;
76pub use turso_orm_macros::DeriveMigrationName;
77
78/// The name of a migration, used as its version key in the bookkeeping table.
79pub trait MigrationName {
80    /// The name, which must be unique across the migrator.
81    fn name(&self) -> &str;
82}
83
84/// A migration: a reversible schema change.
85#[async_trait]
86pub trait MigrationTrait: MigrationName + Send + Sync {
87    /// Applies the migration inside the transaction `manager` wraps.
88    ///
89    /// # Errors
90    ///
91    /// Returns [`DbErr::Driver`] when a statement fails; implementations may
92    /// return any other variant to abort, and the transaction is rolled back.
93    async fn up(&self, manager: &SchemaManager<'_>) -> Result<(), DbErr>;
94
95    /// Reverts the migration inside the transaction `manager` wraps.
96    ///
97    /// # Errors
98    ///
99    /// The default returns [`DbErr::Migration`] unconditionally, marking the
100    /// migration as irreversible; overriding implementations return
101    /// [`DbErr::Driver`] when a statement fails.
102    async fn down(&self, _manager: &SchemaManager<'_>) -> Result<(), DbErr> {
103        Err(DbErr::Migration("this migration cannot be reverted".into()))
104    }
105}
106
107/// Everything a migration module needs, meant to be glob-imported.
108pub mod prelude {
109    pub use crate::{
110        DeriveMigrationName, MigrationIssue, MigrationName, MigrationStatus, MigrationTrait,
111        MigratorTrait, SchemaManager, async_trait,
112    };
113    pub use turso_orm::entity::EntityTrait;
114    pub use turso_orm::sql::{
115        AlterTable, ColumnDef, ColumnType, CreateIndex, CreateTable, DropIndex, DropTable, Expr,
116        ForeignKey, ForeignKeyAction, Order, Table,
117    };
118    pub use turso_orm::{
119        ConnectOptions, ConnectionTrait, Database, DbErr, Schema, Statement, Transaction,
120        TransactionMode, TransactionTrait,
121    };
122}