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}