1use std::fmt;
10use std::hash::Hash;
11use std::marker::PhantomData;
12
13use crate::postgres::{
15 PostgresDDL, PostgresSnapshot, ddl::PostgresEntity, statements::Generator as PostgresGenerator,
16};
17use crate::sqlite::{
18 SQLiteDDL, SQLiteSnapshot, ddl::SqliteEntity, statements::Generator as SqliteGenerator,
19};
20
21pub trait Version: Copy + Clone + Default + 'static {
30 const NUMBER: u32;
32}
33
34#[must_use]
36pub fn version_str<V: Version>() -> String {
37 V::NUMBER.to_string()
38}
39
40#[derive(Copy, Clone, Default, Debug)]
42pub struct V5;
43impl Version for V5 {
44 const NUMBER: u32 = 5;
45}
46
47#[derive(Copy, Clone, Default, Debug)]
49pub struct V6;
50impl Version for V6 {
51 const NUMBER: u32 = 6;
52}
53
54#[derive(Copy, Clone, Default, Debug)]
56pub struct V7;
57impl Version for V7 {
58 const NUMBER: u32 = 7;
59}
60
61#[derive(Copy, Clone, Default, Debug)]
63pub struct V8;
64impl Version for V8 {
65 const NUMBER: u32 = 8;
66}
67
68pub type SqliteLatest = V7;
70pub type PostgresLatest = V8;
71pub type MysqlLatest = V5;
72
73pub trait Upgradable<From: Version, To: Version> {
97 type Output;
99 type Error;
101
102 fn upgrade(self) -> Result<Self::Output, Self::Error>;
110}
111
112#[inline]
133pub const fn assert_can_upgrade<D, From, To>()
134where
135 D: CanUpgrade<From, To>,
136 From: Version,
137 To: Version,
138{
139 }
142
143#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
152#[repr(u8)]
153pub enum EntityKind {
154 Schema = 0,
156 Enum = 1,
157 Sequence = 2,
158 Role = 3,
159
160 Table = 10,
162 Column = 11,
163 Index = 12,
164 ForeignKey = 13,
165 PrimaryKey = 14,
166 UniqueConstraint = 15,
167 CheckConstraint = 16,
168
169 Policy = 20,
171 View = 21,
172}
173
174impl EntityKind {
175 #[must_use]
177 pub const fn as_str(self) -> &'static str {
178 match self {
179 Self::Schema => "schemas",
180 Self::Enum => "enums",
181 Self::Sequence => "sequences",
182 Self::Role => "roles",
183 Self::Table => "tables",
184 Self::Column => "columns",
185 Self::Index => "indexes",
186 Self::ForeignKey => "fks",
187 Self::PrimaryKey => "pks",
188 Self::UniqueConstraint => "uniques",
189 Self::CheckConstraint => "checks",
190 Self::Policy => "policies",
191 Self::View => "views",
192 }
193 }
194
195 #[must_use]
197 pub fn parse(s: &str) -> Option<Self> {
198 match s {
199 "schemas" => Some(Self::Schema),
200 "enums" => Some(Self::Enum),
201 "sequences" => Some(Self::Sequence),
202 "roles" => Some(Self::Role),
203 "tables" => Some(Self::Table),
204 "columns" => Some(Self::Column),
205 "indexes" => Some(Self::Index),
206 "fks" => Some(Self::ForeignKey),
207 "pks" => Some(Self::PrimaryKey),
208 "uniques" => Some(Self::UniqueConstraint),
209 "checks" => Some(Self::CheckConstraint),
210 "policies" => Some(Self::Policy),
211 "views" => Some(Self::View),
212 _ => None,
213 }
214 }
215}
216
217impl std::str::FromStr for EntityKind {
218 type Err = ();
219
220 fn from_str(s: &str) -> Result<Self, Self::Err> {
221 Self::parse(s).ok_or(())
222 }
223}
224
225impl fmt::Display for EntityKind {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 write!(f, "{}", self.as_str())
228 }
229}
230
231#[derive(Clone, Debug, PartialEq, Eq, Hash)]
233pub enum EntityKey {
234 Simple(String),
236 Composite2(String, String),
238 Composite3(String, String, String),
240}
241
242impl EntityKey {
243 pub fn simple(name: impl Into<String>) -> Self {
244 Self::Simple(name.into())
245 }
246
247 pub fn composite2(a: impl Into<String>, b: impl Into<String>) -> Self {
248 Self::Composite2(a.into(), b.into())
249 }
250
251 pub fn composite3(a: impl Into<String>, b: impl Into<String>, c: impl Into<String>) -> Self {
252 Self::Composite3(a.into(), b.into(), c.into())
253 }
254}
255
256pub trait Entity: Clone + PartialEq {
261 const KIND: EntityKind;
263
264 fn key(&self) -> EntityKey;
266
267 fn parent_key(&self) -> Option<EntityKey> {
269 None
270 }
271}
272
273#[derive(Clone, Debug)]
282pub struct Versioned<Data, V: Version> {
283 pub data: Data,
285 _version: PhantomData<V>,
287}
288
289impl<Data, V: Version> Versioned<Data, V> {
290 pub const fn new(data: Data) -> Self {
292 Self {
293 data,
294 _version: PhantomData,
295 }
296 }
297
298 #[must_use]
300 pub const fn version() -> u32 {
301 V::NUMBER
302 }
303
304 #[must_use]
306 pub fn version_str() -> String {
307 version_str::<V>()
308 }
309
310 pub fn into_inner(self) -> Data {
312 self.data
313 }
314}
315
316pub trait Dialect: Sized + 'static {
338 const NAME: &'static str;
340
341 type MinVersion: Version;
343
344 type LatestVersion: Version;
346
347 type Snapshot: Clone + Default + std::fmt::Debug;
349
350 type DDL: Clone + Default + std::fmt::Debug;
352
353 type Entity: Clone + std::fmt::Debug + PartialEq;
355
356 type Generator: Default;
358
359 #[inline]
361 #[must_use]
362 fn is_supported_version(version: u32) -> bool {
363 version >= Self::MinVersion::NUMBER && version <= Self::LatestVersion::NUMBER
364 }
365
366 #[inline]
368 #[must_use]
369 fn is_latest_version(version: u32) -> bool {
370 version == Self::LatestVersion::NUMBER
371 }
372
373 #[inline]
375 #[must_use]
376 fn needs_upgrade_from(version: u32) -> bool {
377 version < Self::LatestVersion::NUMBER && version >= Self::MinVersion::NUMBER
378 }
379
380 fn diff_and_generate(
382 prev: &Self::Snapshot,
383 cur: &Self::Snapshot,
384 breakpoints: bool,
385 ) -> DiffResult;
386}
387
388pub trait CanUpgrade<From: Version, To: Version>: Dialect {}
413
414#[derive(Debug, Clone)]
420pub struct DiffResult {
421 pub sql_statements: Vec<String>,
423 pub has_changes: bool,
425}
426
427impl DiffResult {
428 #[must_use]
430 pub const fn empty() -> Self {
431 Self {
432 sql_statements: Vec::new(),
433 has_changes: false,
434 }
435 }
436
437 #[must_use]
439 pub const fn with_changes(sql_statements: Vec<String>) -> Self {
440 let has_changes = !sql_statements.is_empty();
441 Self {
442 sql_statements,
443 has_changes,
444 }
445 }
446}
447
448#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
450pub struct Sqlite;
451
452impl Sqlite {
453 pub const MIN_VERSION: u32 = V5::NUMBER;
455 pub const LATEST_VERSION: u32 = V7::NUMBER;
457}
458
459impl Dialect for Sqlite {
460 const NAME: &'static str = "sqlite";
461 type MinVersion = V5;
462 type LatestVersion = V7;
463 type Snapshot = SQLiteSnapshot;
464 type DDL = SQLiteDDL;
465 type Entity = SqliteEntity;
466 type Generator = SqliteGenerator;
467
468 fn diff_and_generate(
469 prev: &Self::Snapshot,
470 cur: &Self::Snapshot,
471 breakpoints: bool,
472 ) -> DiffResult {
473 let diff = crate::sqlite::diff_snapshots(prev, cur);
474 if !diff.has_changes() {
475 return DiffResult::empty();
476 }
477 let generator = SqliteGenerator::new().with_breakpoints(breakpoints);
478 let sql = generator.generate_migration(&diff);
479 DiffResult::with_changes(sql)
480 }
481}
482
483impl CanUpgrade<V5, V6> for Sqlite {}
485impl CanUpgrade<V6, V7> for Sqlite {}
486impl CanUpgrade<V5, V7> for Sqlite {}
488
489#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
491pub struct Postgres;
492
493impl Postgres {
494 pub const MIN_VERSION: u32 = V5::NUMBER;
496 pub const LATEST_VERSION: u32 = V8::NUMBER;
498}
499
500impl Dialect for Postgres {
501 const NAME: &'static str = "postgresql";
502 type MinVersion = V5;
503 type LatestVersion = V8;
504 type Snapshot = PostgresSnapshot;
505 type DDL = PostgresDDL;
506 type Entity = PostgresEntity;
507 type Generator = PostgresGenerator;
508
509 fn diff_and_generate(
510 prev: &Self::Snapshot,
511 cur: &Self::Snapshot,
512 breakpoints: bool,
513 ) -> DiffResult {
514 let diff = crate::postgres::diff_snapshots(&prev.ddl, &cur.ddl);
515 if !diff.has_changes() {
516 return DiffResult::empty();
517 }
518 let generator = PostgresGenerator::new().with_breakpoints(breakpoints);
519 let sql = generator.generate(&diff.diffs);
520 DiffResult::with_changes(sql)
521 }
522}
523
524impl CanUpgrade<V5, V6> for Postgres {}
526impl CanUpgrade<V6, V7> for Postgres {}
527impl CanUpgrade<V7, V8> for Postgres {}
528impl CanUpgrade<V5, V7> for Postgres {}
530impl CanUpgrade<V5, V8> for Postgres {}
531impl CanUpgrade<V6, V8> for Postgres {}
532
533#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
535pub struct Mysql;
536
537impl Mysql {
538 pub const MIN_VERSION: u32 = V5::NUMBER;
540 pub const LATEST_VERSION: u32 = V5::NUMBER;
542}
543
544impl Dialect for Mysql {
545 const NAME: &'static str = "mysql";
546 type MinVersion = V5;
547 type LatestVersion = V5;
548 type Snapshot = ();
550 type DDL = ();
551 type Entity = ();
552 type Generator = ();
553
554 fn diff_and_generate(
555 _prev: &Self::Snapshot,
556 _cur: &Self::Snapshot,
557 _breakpoints: bool,
558 ) -> DiffResult {
559 unimplemented!("MySQL migrations not yet supported")
560 }
561}
562#[derive(Copy, Clone, Debug, PartialEq, Eq)]
570pub enum DiffType {
571 Create,
572 Drop,
573 Alter,
574}
575
576#[cfg(test)]
577mod tests {
578 use std::str::FromStr;
579
580 use super::*;
581
582 #[test]
583 fn test_version_numbers() {
584 assert_eq!(V5::NUMBER, 5);
585 assert_eq!(V6::NUMBER, 6);
586 assert_eq!(V7::NUMBER, 7);
587 assert_eq!(V8::NUMBER, 8);
588 }
589
590 #[test]
591 fn test_version_str() {
592 assert_eq!(version_str::<V5>(), "5");
593 assert_eq!(version_str::<V7>(), "7");
594 }
595
596 #[test]
597 fn test_entity_kind_str() {
598 assert_eq!(EntityKind::Table.as_str(), "tables");
599 assert_eq!(EntityKind::Column.as_str(), "columns");
600 assert_eq!(EntityKind::ForeignKey.as_str(), "fks");
601 }
602
603 #[test]
604 fn test_entity_kind_parse() {
605 assert_eq!(EntityKind::from_str("tables"), Ok(EntityKind::Table));
606 assert_eq!(EntityKind::from_str("columns"), Ok(EntityKind::Column));
607 assert_eq!(EntityKind::from_str("invalid"), Err(()));
608 }
609
610 #[test]
611 fn test_versioned_snapshot() {
612 #[derive(Clone, Debug)]
613 struct TestData {
614 value: i32,
615 }
616
617 let versioned: Versioned<TestData, V7> = Versioned::new(TestData { value: 42 });
618 assert_eq!(Versioned::<TestData, V7>::version(), 7);
619 assert_eq!(versioned.data.value, 42);
620 }
621
622 #[test]
623 fn test_dialect_version_info() {
624 assert_eq!(Sqlite::MIN_VERSION, 5);
626 assert_eq!(Sqlite::LATEST_VERSION, 7);
627
628 assert_eq!(Postgres::MIN_VERSION, 5);
630 assert_eq!(Postgres::LATEST_VERSION, 8);
631
632 assert_eq!(Mysql::MIN_VERSION, 5);
634 assert_eq!(Mysql::LATEST_VERSION, 5);
635 }
636
637 #[test]
638 fn test_dialect_version_checks() {
639 assert!(Sqlite::is_supported_version(5));
641 assert!(Sqlite::is_supported_version(6));
642 assert!(Sqlite::is_supported_version(7));
643 assert!(!Sqlite::is_supported_version(4));
644 assert!(!Sqlite::is_supported_version(8));
645
646 assert!(Sqlite::needs_upgrade_from(5));
647 assert!(Sqlite::needs_upgrade_from(6));
648 assert!(!Sqlite::needs_upgrade_from(7));
649
650 assert!(!Sqlite::is_latest_version(5));
651 assert!(Sqlite::is_latest_version(7));
652 }
653
654 #[test]
655 fn test_can_upgrade_compiles() {
656 assert_can_upgrade::<Sqlite, V5, V6>();
659 assert_can_upgrade::<Sqlite, V6, V7>();
660 assert_can_upgrade::<Sqlite, V5, V7>(); assert_can_upgrade::<Postgres, V5, V6>();
663 assert_can_upgrade::<Postgres, V6, V7>();
664 assert_can_upgrade::<Postgres, V7, V8>();
665 assert_can_upgrade::<Postgres, V5, V8>(); }
667
668 }