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