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 columns;
19mod indices;
20mod schema;
21mod tables;
22mod types;
23
24pub use columns::{Columns, ColumnsItem};
25pub use indices::{Indices, IndicesItem};
26pub use schema::Schema;
27pub use tables::{Tables, TablesItem};
28pub use types::{Types, TypesItem};
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/// assert_eq!(hints.get_table(TableId(0)), Some(TableId(1)));
48/// assert_eq!(hints.get_table(TableId(2)), None);
49/// ```
50#[derive(Default)]
51pub struct RenameHints {
52 tables: HashMap<TableId, TableId>,
53 columns: HashMap<ColumnId, ColumnId>,
54 indices: HashMap<IndexId, IndexId>,
55}
56
57impl RenameHints {
58 /// Creates an empty set of rename hints.
59 pub fn new() -> Self {
60 Self::default()
61 }
62
63 /// Records that the table previously identified by `from` is now identified by `to`.
64 pub fn add_table_hint(&mut self, from: TableId, to: TableId) {
65 self.tables.insert(from, to);
66 }
67
68 /// Records that the column previously identified by `from` is now identified by `to`.
69 pub fn add_column_hint(&mut self, from: ColumnId, to: ColumnId) {
70 self.columns.insert(from, to);
71 }
72
73 /// Records that the index previously identified by `from` is now identified by `to`.
74 pub fn add_index_hint(&mut self, from: IndexId, to: IndexId) {
75 self.indices.insert(from, to);
76 }
77
78 /// Returns the new [`TableId`] if a rename hint exists for `from`.
79 pub fn get_table(&self, from: TableId) -> Option<TableId> {
80 self.tables.get(&from).copied()
81 }
82
83 /// Returns the new [`ColumnId`] if a rename hint exists for `from`.
84 pub fn get_column(&self, from: ColumnId) -> Option<ColumnId> {
85 self.columns.get(&from).copied()
86 }
87
88 /// Returns the new [`IndexId`] if a rename hint exists for `from`.
89 pub fn get_index(&self, from: IndexId) -> Option<IndexId> {
90 self.indices.get(&from).copied()
91 }
92}
93
94/// Shared context passed to all diff computations.
95///
96/// Holds references to both the previous and next [`db::Schema`](super::db::Schema)
97/// versions and the [`RenameHints`] that guide rename detection.
98///
99/// # Examples
100///
101/// ```ignore
102/// use toasty_core::schema::{db, diff};
103///
104/// let previous = db::Schema::default();
105/// let next = db::Schema::default();
106/// let hints = diff::RenameHints::new();
107/// let cx = diff::Context::new(&previous, &next, &hints);
108/// assert!(cx.previous().tables.is_empty());
109/// ```
110pub struct Context<'a> {
111 previous: &'a DbSchema,
112 next: &'a DbSchema,
113
114 rename_hints: &'a RenameHints,
115}
116
117impl<'a> Context<'a> {
118 /// Creates a new diff context from the previous schema, the next schema,
119 /// and the rename hints that map old IDs to new IDs.
120 pub fn new(previous: &'a DbSchema, next: &'a DbSchema, rename_hints: &'a RenameHints) -> Self {
121 Self {
122 previous,
123 next,
124 rename_hints,
125 }
126 }
127
128 /// Returns the rename hints for this diff.
129 pub fn rename_hints(&self) -> &'a RenameHints {
130 self.rename_hints
131 }
132
133 /// Returns the schema before the change.
134 pub fn previous(&self) -> &'a DbSchema {
135 self.previous
136 }
137
138 /// Returns the schema after the change.
139 pub fn next(&self) -> &'a DbSchema {
140 self.next
141 }
142}