toasty_core/schema/diff.rs
1//! Schema-diff types.
2//!
3//! Compares two [`db::Schema`](super::db::Schema) versions and produces
4//! structured changes consumed by drivers to generate migrations.
5//!
6//! # Examples
7//!
8//! ```ignore
9//! use toasty_core::schema::{db, diff};
10//!
11//! let previous = db::Schema::default();
12//! let next = db::Schema::default();
13//! let hints = diff::RenameHints::new();
14//! let d = diff::Schema::from(&previous, &next, &hints);
15//! assert!(d.is_empty());
16//! ```
17
18mod column;
19mod index;
20mod schema;
21mod table;
22mod ty;
23
24pub use column::Column;
25pub use index::Index;
26pub use schema::Schema;
27pub use table::Table;
28pub use ty::Type;
29
30use hashbrown::HashMap;
31
32use crate::schema::db::{ColumnId, IndexId, Schema as DbSchema, TableId};
33
34/// Hints that tell the diff algorithm which schema items were renamed.
35///
36/// Without rename hints, a renamed table/column/index appears as a drop
37/// followed by a create. Adding a hint maps the old ID to the new ID so
38/// the diff produces an alter instead.
39///
40/// # Examples
41///
42/// ```ignore
43/// use toasty_core::schema::{db::TableId, diff};
44///
45/// let mut hints = diff::RenameHints::new();
46/// hints.add_table_hint(TableId(0), TableId(1));
47/// ```
48#[derive(Default)]
49pub struct RenameHints {
50 tables: HashMap<TableId, TableId>,
51 columns: HashMap<ColumnId, ColumnId>,
52 indices: HashMap<IndexId, IndexId>,
53}
54
55impl RenameHints {
56 /// Creates an empty set of rename hints.
57 pub fn new() -> Self {
58 Self::default()
59 }
60
61 /// Records that the table previously identified by `from` is now identified by `to`.
62 pub fn add_table_hint(&mut self, from: TableId, to: TableId) {
63 self.tables.insert(from, to);
64 }
65
66 /// Records that the column previously identified by `from` is now identified by `to`.
67 pub fn add_column_hint(&mut self, from: ColumnId, to: ColumnId) {
68 self.columns.insert(from, to);
69 }
70
71 /// Records that the index previously identified by `from` is now identified by `to`.
72 pub fn add_index_hint(&mut self, from: IndexId, to: IndexId) {
73 self.indices.insert(from, to);
74 }
75
76 /// Returns the new [`TableId`] if a rename hint exists for `from`.
77 pub(super) fn get_table(&self, from: TableId) -> Option<TableId> {
78 self.tables.get(&from).copied()
79 }
80
81 /// Returns the new [`ColumnId`] if a rename hint exists for `from`.
82 pub(super) fn get_column(&self, from: ColumnId) -> Option<ColumnId> {
83 self.columns.get(&from).copied()
84 }
85
86 /// Returns the new [`IndexId`] if a rename hint exists for `from`.
87 pub(super) fn get_index(&self, from: IndexId) -> Option<IndexId> {
88 self.indices.get(&from).copied()
89 }
90}
91
92/// Shared context passed to all diff computations.
93///
94/// Holds references to both the previous and next [`db::Schema`](super::db::Schema)
95/// versions and the [`RenameHints`] that guide rename detection.
96///
97/// # Examples
98///
99/// ```ignore
100/// use toasty_core::schema::{db, diff};
101///
102/// let previous = db::Schema::default();
103/// let next = db::Schema::default();
104/// let hints = diff::RenameHints::new();
105/// let cx = diff::Context::new(&previous, &next, &hints);
106/// assert!(cx.next().tables.is_empty());
107/// ```
108pub struct Context<'a> {
109 previous: &'a DbSchema,
110 next: &'a DbSchema,
111
112 rename_hints: &'a RenameHints,
113}
114
115impl<'a> Context<'a> {
116 /// Returns the rename hints for this diff.
117 pub(super) fn rename_hints(&self) -> &'a RenameHints {
118 self.rename_hints
119 }
120
121 /// Returns the schema before the change.
122 pub(super) fn previous(&self) -> &'a DbSchema {
123 self.previous
124 }
125
126 /// Creates a new diff context from the previous schema, the next schema,
127 /// and the rename hints that map old IDs to new IDs.
128 pub fn new(previous: &'a DbSchema, next: &'a DbSchema, rename_hints: &'a RenameHints) -> Self {
129 Self {
130 previous,
131 next,
132 rename_hints,
133 }
134 }
135
136 /// Returns the schema after the change.
137 pub fn next(&self) -> &'a DbSchema {
138 self.next
139 }
140}