Skip to content

Rails style migrations #1766

@pynixwang

Description

@pynixwang

current data type is a confusion, String, StringNull, StringUniq

I chat with ChatGPT, it gives me a lib

really work.

use sea_orm_migration::prelude::*;
use sea_orm_migration::sea_orm::DbErr;

#[derive(Debug, Clone)]
pub enum ColumnType {
    Integer,
    BigInteger,
    String,
    Text,
    Boolean,
    DateTime,
    Timestamp,
    Uuid,
    Json,
}

#[derive(Debug, Clone)]
pub struct Column {
    pub name: &'static str,
    pub ty: ColumnType,
    pub nullable: bool,
    pub primary_key: bool,
    pub unique: bool,
}

impl Column {
    pub const fn new(
        name: &'static str,
        ty: ColumnType,
        nullable: bool,
        primary_key: bool,
        unique: bool,
    ) -> Self {
        Self {
            name,
            ty,
            nullable,
            primary_key,
            unique,
        }
    }
}

#[derive(Debug, Clone)]
struct DynIden(String);

impl DynIden {
    fn new(name: impl Into<String>) -> Self {
        Self(name.into())
    }
}

impl Iden for DynIden {
    fn unquoted(&self, s: &mut dyn std::fmt::Write) {
        write!(s, "{}", self.0).unwrap();
    }
}


/// ```ignore
/// create_table(
///     manager,
///     "users",
///     &[
///         col!(id: integer, primary_key: true),
///         col!(email: string, nullable: false, unique: true),
///         col!(name: string),
///         col!(created_at: timestamp, nullable: false),
///     ],
/// )
/// .await?;
/// ```
pub async fn create_table(
    m: &SchemaManager<'_>,
    table: &str,
    cols: &[Column],
    timestamps: bool,
) -> Result<(), DbErr> {
    let mut stmt = Table::create();

    stmt.table(DynIden::new(table)).if_not_exists();

    for col in cols {
        stmt.col(build_column_def(col));
    }

    if timestamps {
        stmt.col(build_column_def(&Column::new(
            "created_at",
            ColumnType::Timestamp,
            false,
            false,
            false,
        )));

        stmt.col(build_column_def(&Column::new(
            "updated_at",
            ColumnType::Timestamp,
            false,
            false,
            false,
        )));
    }

    m.create_table(stmt).await
}

pub async fn create_table_with_timestamps(
    m: &SchemaManager<'_>,
    table: &str,
    cols: &[Column],
) -> Result<(), DbErr> {
    create_table(m, table, cols, true).await
}

fn build_column_def(col: &Column) -> ColumnDef {
    let mut def = ColumnDef::new(DynIden::new(col.name));

    match col.ty {
        ColumnType::Integer => {
            def.integer();
        }
        ColumnType::BigInteger => {
            def.big_integer();
        }
        ColumnType::String => {
            def.string();
        }
        ColumnType::Text => {
            def.text();
        }
        ColumnType::Boolean => {
            def.boolean();
        }
        ColumnType::DateTime => {
            def.date_time();
        }
        ColumnType::Timestamp => {
            def.timestamp();
        }
        ColumnType::Uuid => {
            def.uuid();
        }
        ColumnType::Json => {
            def.json();
        }
    }

    if col.primary_key {
        def.not_null();
        def.primary_key();

        match col.ty {
            ColumnType::Integer | ColumnType::BigInteger => {
                def.auto_increment();
            }
            _ => {}
        }
    } else if !col.nullable {
        def.not_null();
    }

    if col.unique && !col.primary_key {
        def.unique_key();
    }

    def
}


/// ```ignore
/// col!(id: integer)
/// col!(email: string, nullable: false)
/// col!(email: string, nullable: false, unique: true)
/// col!(id: integer, nullable: false, primary_key: true, unique: true)
/// ```
#[macro_export]
macro_rules! col {
    (
        $name:ident : $ty:ident
        $(, nullable: $nullable:expr)?
        $(, primary_key: $primary_key:expr)?
        $(, unique: $unique:expr)?
        $(,)?
    ) => {
        $crate::Column::new(
            stringify!($name),
            $crate::col_type!($ty),
            $crate::__col_or_default_bool!(true $(, $nullable)?),
            $crate::__col_or_default_bool!(false $(, $primary_key)?),
            $crate::__col_or_default_bool!(false $(, $unique)?),
        )
    };
}

#[macro_export]
macro_rules! col_type {
    (integer) => {
        $crate::ColumnType::Integer
    };
    (int) => {
        $crate::ColumnType::Integer
    };
    (big_integer) => {
        $crate::ColumnType::BigInteger
    };
    (bigint) => {
        $crate::ColumnType::BigInteger
    };
    (string) => {
        $crate::ColumnType::String
    };
    (varchar) => {
        $crate::ColumnType::String
    };
    (text) => {
        $crate::ColumnType::Text
    };
    (bool) => {
        $crate::ColumnType::Boolean
    };
    (boolean) => {
        $crate::ColumnType::Boolean
    };
    (datetime) => {
        $crate::ColumnType::DateTime
    };
    (date_time) => {
        $crate::ColumnType::DateTime
    };
    (timestamp) => {
        $crate::ColumnType::Timestamp
    };
    (uuid) => {
        $crate::ColumnType::Uuid
    };
    (json) => {
        $crate::ColumnType::Json
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! __col_or_default_bool {
    ($default:expr) => {
        $default
    };
    ($default:expr, $value:expr) => {
        $value
    };
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions