toasty_core/schema/diff/tables.rs
1use super::{Columns, Context, Indices};
2use crate::schema::db::Table;
3
4use hashbrown::{HashMap, HashSet};
5use std::ops::Deref;
6
7/// The set of differences between two table lists.
8///
9/// Computed by [`Tables::from`] and dereferences to `Vec<TablesItem>` for
10/// iteration.
11///
12/// # Examples
13///
14/// ```ignore
15/// use toasty_core::schema::{db, diff};
16///
17/// let previous = db::Schema::default();
18/// let next = db::Schema::default();
19/// let hints = diff::RenameHints::new();
20/// let cx = diff::Context::new(&previous, &next, &hints);
21/// let d = diff::Tables::from(&cx, &[], &[]);
22/// assert!(d.is_empty());
23/// ```
24pub struct Tables<'a> {
25 items: Vec<TablesItem<'a>>,
26}
27
28impl<'a> Tables<'a> {
29 /// Computes the diff between two table slices.
30 ///
31 /// Uses [`Context`] to resolve rename hints. Tables matched by name (or
32 /// by rename hint) are compared for column and index changes; unmatched
33 /// tables in `previous` become drops, and unmatched tables in `next`
34 /// become creates.
35 pub fn from(cx: &Context<'a>, previous: &'a [Table], next: &'a [Table]) -> Self {
36 let mut items = vec![];
37 let mut create_ids: HashSet<_> = next.iter().map(|next| next.id).collect();
38
39 let next_map = HashMap::<&str, &'a Table>::from_iter(
40 next.iter().map(|next| (next.name.as_str(), next)),
41 );
42
43 for previous in previous {
44 let next = if let Some(next_id) = cx.rename_hints().get_table(previous.id) {
45 cx.next().table(next_id)
46 } else if let Some(to) = next_map.get(previous.name.as_str()) {
47 to
48 } else {
49 items.push(TablesItem::DropTable(previous));
50 continue;
51 };
52
53 create_ids.remove(&next.id);
54
55 let columns = Columns::from(cx, &previous.columns, &next.columns);
56 let indices = Indices::from(cx, &previous.indices, &next.indices);
57 if previous.name != next.name || !columns.is_empty() || !indices.is_empty() {
58 items.push(TablesItem::AlterTable {
59 previous,
60 next,
61 columns,
62 indices,
63 });
64 }
65 }
66
67 for table_id in create_ids {
68 items.push(TablesItem::CreateTable(cx.next().table(table_id)));
69 }
70
71 Self { items }
72 }
73}
74
75impl<'a> Deref for Tables<'a> {
76 type Target = Vec<TablesItem<'a>>;
77
78 fn deref(&self) -> &Self::Target {
79 &self.items
80 }
81}
82
83/// A single change detected between two table lists.
84pub enum TablesItem<'a> {
85 /// A new table was created.
86 CreateTable(&'a Table),
87 /// An existing table was dropped.
88 DropTable(&'a Table),
89 /// A table was modified (name, columns, or indices changed).
90 AlterTable {
91 /// The table definition before the change.
92 previous: &'a Table,
93 /// The table definition after the change.
94 next: &'a Table,
95 /// Column-level changes within this table.
96 columns: Columns<'a>,
97 /// Index-level changes within this table.
98 indices: Indices<'a>,
99 },
100}