Skip to main content

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}