toasty_core/schema/db/table.rs
1use super::{Column, ColumnId, Index, IndexId, PrimaryKey};
2use crate::stmt;
3
4use std::fmt;
5
6/// A database table with its columns, primary key, and indices.
7///
8/// # Examples
9///
10/// ```ignore
11/// use toasty_core::schema::db::{Table, TableId};
12///
13/// let table = Table::new(TableId(0), "users".to_string());
14/// assert_eq!(table.name, "users");
15/// assert!(table.columns.is_empty());
16/// ```
17#[derive(Debug, Clone)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub struct Table {
20 /// Uniquely identifies a table within the schema.
21 pub id: TableId,
22
23 /// Name of the table as it appears in the database.
24 pub name: String,
25
26 /// The table's columns, in order.
27 pub columns: Vec<Column>,
28
29 /// The table's primary key definition.
30 pub primary_key: PrimaryKey,
31
32 /// Secondary indices on this table.
33 pub indices: Vec<Index>,
34}
35
36/// Uniquely identifies a table within a [`Schema`](super::Schema).
37///
38/// The inner `usize` is a zero-based index into [`Schema::tables`](super::Schema::tables).
39///
40/// # Examples
41///
42/// ```ignore
43/// use toasty_core::schema::db::TableId;
44///
45/// let id = TableId(0);
46/// assert_eq!(id.0, 0);
47/// ```
48#[derive(PartialEq, Eq, Clone, Copy, Hash)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub struct TableId(pub usize);
51
52impl Table {
53 /// Returns the `i`-th column of this table's primary key.
54 ///
55 /// # Panics
56 ///
57 /// Panics if `i` is out of bounds for the primary key column list.
58 pub fn primary_key_column(&self, i: usize) -> &Column {
59 &self.columns[self.primary_key.columns[i].index]
60 }
61
62 /// Returns an iterator over the columns that make up this table's primary key.
63 pub fn primary_key_columns(&self) -> impl ExactSizeIterator<Item = &Column> + '_ {
64 self.primary_key
65 .columns
66 .iter()
67 .map(|column_id| &self.columns[column_id.index])
68 }
69
70 /// Returns the column identified by `id`.
71 ///
72 /// Only the column's `index` field is used; the `table` component is ignored.
73 ///
74 /// # Panics
75 ///
76 /// Panics if the column index is out of bounds.
77 pub fn column(&self, id: impl Into<ColumnId>) -> &Column {
78 &self.columns[id.into().index]
79 }
80
81 /// Resolves a single-step [`Projection`](stmt::Projection) to a column.
82 ///
83 /// # Panics
84 ///
85 /// Panics if the projection is empty or contains more than one step.
86 pub fn resolve(&self, projection: &stmt::Projection) -> &Column {
87 let [first, rest @ ..] = projection.as_slice() else {
88 panic!("need at most one path step")
89 };
90 assert!(rest.is_empty());
91
92 &self.columns[*first]
93 }
94
95 pub(crate) fn new(id: TableId, name: String) -> Self {
96 Self {
97 id,
98 name,
99 columns: vec![],
100 primary_key: PrimaryKey {
101 columns: vec![],
102 index: IndexId {
103 table: id,
104 index: 0,
105 },
106 },
107 indices: vec![],
108 }
109 }
110}
111
112impl TableId {
113 pub(crate) fn placeholder() -> Self {
114 Self(usize::MAX)
115 }
116}
117
118impl fmt::Debug for TableId {
119 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
120 write!(fmt, "TableId({})", self.0)
121 }
122}