toasty_core/schema/db/
schema.rs

1use super::{
2    Column, ColumnId, DiffContext, Index, IndexId, RenameHints, Table, TableId, TablesDiff,
3};
4
5/// The complete database-level schema: a collection of tables.
6///
7/// Provides indexed access to tables, columns, and indices by their IDs.
8///
9/// # Examples
10///
11/// ```ignore
12/// use toasty_core::schema::db::Schema;
13///
14/// let schema = Schema::default();
15/// assert!(schema.tables.is_empty());
16/// ```
17#[derive(Debug, Default, Clone)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub struct Schema {
20    /// All tables in this schema.
21    pub tables: Vec<Table>,
22}
23
24impl Schema {
25    /// Returns the column identified by `id`.
26    ///
27    /// # Panics
28    ///
29    /// Panics if the table or column index is out of bounds.
30    pub fn column(&self, id: impl Into<ColumnId>) -> &Column {
31        let id = id.into();
32        self.table(id.table)
33            .columns
34            .get(id.index)
35            .expect("invalid column ID")
36    }
37
38    /// Returns a mutable reference to the column identified by `id`.
39    ///
40    /// # Panics
41    ///
42    /// Panics if the table or column index is out of bounds.
43    pub fn column_mut(&mut self, id: impl Into<ColumnId>) -> &mut Column {
44        let id = id.into();
45        self.table_mut(id.table)
46            .columns
47            .get_mut(id.index)
48            .expect("invalid column ID")
49    }
50
51    /// Returns the index identified by `id`.
52    ///
53    /// # Panics
54    ///
55    /// Panics if the table or index offset is out of bounds.
56    // NOTE: this is unlikely to confuse users given the context.
57    #[allow(clippy::should_implement_trait)]
58    pub fn index(&self, id: IndexId) -> &Index {
59        self.table(id.table)
60            .indices
61            .get(id.index)
62            .expect("invalid index ID")
63    }
64
65    /// Returns a mutable reference to the index identified by `id`.
66    ///
67    /// # Panics
68    ///
69    /// Panics if the table or index offset is out of bounds.
70    // NOTE: this is unlikely to confuse users given the context.
71    #[allow(clippy::should_implement_trait)]
72    pub fn index_mut(&mut self, id: IndexId) -> &mut Index {
73        self.table_mut(id.table)
74            .indices
75            .get_mut(id.index)
76            .expect("invalid index ID")
77    }
78
79    /// Returns the table identified by `id`.
80    ///
81    /// # Panics
82    ///
83    /// Panics if the table index is out of bounds.
84    pub fn table(&self, id: impl Into<TableId>) -> &Table {
85        self.tables.get(id.into().0).expect("invalid table ID")
86    }
87
88    /// Returns a mutable reference to the table identified by `id`.
89    ///
90    /// # Panics
91    ///
92    /// Panics if the table index is out of bounds.
93    pub fn table_mut(&mut self, id: impl Into<TableId>) -> &mut Table {
94        self.tables.get_mut(id.into().0).expect("invalid table ID")
95    }
96}
97
98/// The top-level diff between two database schemas.
99///
100/// Contains a [`TablesDiff`] describing created, dropped, and altered tables.
101/// Constructed via [`SchemaDiff::from`].
102///
103/// # Examples
104///
105/// ```ignore
106/// use toasty_core::schema::db::{SchemaDiff, RenameHints, Schema};
107///
108/// let previous = Schema::default();
109/// let next = Schema::default();
110/// let hints = RenameHints::new();
111/// let diff = SchemaDiff::from(&previous, &next, &hints);
112/// assert!(diff.is_empty());
113/// ```
114pub struct SchemaDiff<'a> {
115    previous: &'a Schema,
116    next: &'a Schema,
117    tables: TablesDiff<'a>,
118}
119
120impl<'a> SchemaDiff<'a> {
121    /// Computes the diff between two schemas, using the provided rename hints.
122    pub fn from(from: &'a Schema, to: &'a Schema, rename_hints: &'a RenameHints) -> Self {
123        let cx = &DiffContext::new(from, to, rename_hints);
124        Self {
125            previous: from,
126            next: to,
127            tables: TablesDiff::from(cx, &from.tables, &to.tables),
128        }
129    }
130
131    /// Returns the table-level diff.
132    pub fn tables(&self) -> &TablesDiff<'a> {
133        &self.tables
134    }
135
136    /// Returns `true` if no tables were created, dropped, or altered.
137    pub fn is_empty(&self) -> bool {
138        self.tables.is_empty()
139    }
140
141    /// Returns the schema before the change.
142    pub fn previous(&self) -> &'a Schema {
143        self.previous
144    }
145
146    /// Returns the schema after the change.
147    pub fn next(&self) -> &'a Schema {
148        self.next
149    }
150}