#[derive(Debug, Clone)]
pub struct Migration {
pub id: String,
pub description: String,
pub up_sql: String,
pub down_sql: String,
}
#[derive(Debug, Clone)]
pub struct ModelSnapshot {
pub migration_id: String,
pub entity_types: Vec<SnapshotEntityType>,
}
#[derive(Debug, Clone)]
pub struct SnapshotEntityType {
pub type_name: String,
pub table_name: String,
pub columns: Vec<SnapshotColumn>,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SnapshotColumn {
pub field_name: String,
pub column_name: String,
pub type_name: String,
pub is_primary_key: bool,
pub is_required: bool,
pub is_foreign_key: bool,
pub max_length: Option<usize>,
pub is_auto_increment: bool,
pub is_sequence: bool,
pub sequence_name: Option<String>,
pub fk_referenced_table: Option<String>,
pub fk_referenced_column: Option<String>,
pub has_index: bool,
pub is_unique: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MigrationDialect {
Postgres,
MySql,
Sqlite,
}
impl MigrationDialect {
pub fn quote(&self, ident: &str) -> String {
match self {
MigrationDialect::Postgres | MigrationDialect::Sqlite => format!("\"{}\"", ident),
MigrationDialect::MySql => format!("`{}`", ident),
}
}
pub fn map_column_type(&self, col: &SnapshotColumn) -> String {
let tn = col.type_name.as_str();
if col.is_auto_increment {
if tn.ends_with("i32") {
return match self {
MigrationDialect::Postgres => "SERIAL".into(),
MigrationDialect::MySql => "INT AUTO_INCREMENT".into(),
MigrationDialect::Sqlite => "INTEGER".into(),
};
}
if tn.ends_with("i64") {
return match self {
MigrationDialect::Postgres => "BIGSERIAL".into(),
MigrationDialect::MySql => "BIGINT AUTO_INCREMENT".into(),
MigrationDialect::Sqlite => "INTEGER".into(),
};
}
}
if col.is_sequence {
if tn.ends_with("i32") {
return match self {
MigrationDialect::Postgres => "INTEGER".into(),
MigrationDialect::MySql => "INT AUTO_INCREMENT".into(),
MigrationDialect::Sqlite => "INTEGER".into(),
};
}
if tn.ends_with("i64") {
return match self {
MigrationDialect::Postgres => "BIGINT".into(),
MigrationDialect::MySql => "BIGINT AUTO_INCREMENT".into(),
MigrationDialect::Sqlite => "INTEGER".into(),
};
}
}
let base: &str = if tn.ends_with("i16") {
"SMALLINT"
} else if tn.ends_with("i32") {
"INTEGER"
} else if tn.ends_with("i64") {
"BIGINT"
} else if tn.ends_with("f32") {
"REAL"
} else if tn.ends_with("f64") {
"DOUBLE PRECISION"
} else if tn.ends_with("bool") {
"BOOLEAN"
} else if tn.ends_with("String") {
return match col.max_length {
Some(n) => format!("VARCHAR({})", n),
None => "TEXT".into(),
};
} else if tn.ends_with("Vec<u8>") {
return match self {
MigrationDialect::Postgres => "BYTEA".into(),
MigrationDialect::MySql | MigrationDialect::Sqlite => "BLOB".into(),
};
} else if tn.contains("NaiveDateTime") {
return match self {
MigrationDialect::Postgres => "TIMESTAMP".into(),
MigrationDialect::MySql => "DATETIME".into(),
MigrationDialect::Sqlite => "TEXT".into(),
};
} else if tn.contains("NaiveDate") {
return match self {
MigrationDialect::Postgres => "DATE".into(),
MigrationDialect::MySql => "DATE".into(),
MigrationDialect::Sqlite => "TEXT".into(),
};
} else if tn.contains("DateTime") {
return match self {
MigrationDialect::Postgres => "TIMESTAMPTZ".into(),
MigrationDialect::MySql => "DATETIME".into(),
MigrationDialect::Sqlite => "TEXT".into(),
};
} else if tn.contains("Uuid") {
return match self {
MigrationDialect::Postgres => "UUID".into(),
MigrationDialect::MySql => "CHAR(36)".into(),
MigrationDialect::Sqlite => "TEXT".into(),
};
} else if tn.contains("Decimal") {
return match self {
MigrationDialect::Postgres => "NUMERIC".into(),
MigrationDialect::MySql => "DECIMAL(38,18)".into(),
MigrationDialect::Sqlite => "TEXT".into(),
};
} else {
"TEXT"
};
base.to_string()
}
}
#[derive(Debug, Clone)]
pub(crate) enum SchemaChange {
CreateTable {
table: String,
columns: Vec<SnapshotColumn>,
},
DropTable {
table: String,
},
AddColumn {
table: String,
column: SnapshotColumn,
},
DropColumn {
table: String,
column_name: String,
},
AlterColumn {
table: String,
column_name: String,
old: SnapshotColumn,
new: SnapshotColumn,
},
AddForeignKey {
table: String,
column: String,
referenced_table: String,
referenced_column: String,
},
DropForeignKey {
table: String,
column: String,
referenced_table: String,
},
CreateIndex {
table: String,
column: String,
is_unique: bool,
},
DropIndex {
table: String,
column: String,
is_unique: bool,
},
}
#[derive(Debug, Clone)]
pub struct MigrationHistoryEntry {
pub migration_id: String,
pub product_version: String,
}
pub const MIGRATION_HISTORY_TABLE: &str = "__ef_migrations_history";
pub const PRODUCT_VERSION: &str = env!("CARGO_PKG_VERSION");