Skip to main content

drizzle_core/traits/
table.rs

1use crate::prelude::*;
2use crate::{ColumnRef, SQL, SQLParam, SQLSchema, SQLSchemaType, TableRef, ToSQL};
3use core::marker::PhantomData;
4use core::ops::Deref;
5
6/// Update-model state: no field set yet. Such a model cannot be passed to
7/// `.set(...)`, so an `UPDATE` with an empty `SET` does not compile.
8pub struct Empty;
9/// Update-model state: at least one field set.
10pub struct NonEmpty;
11
12/// A type that stands for a SQL name, used to name aliases, CTEs, derived
13/// tables and named expressions.
14///
15/// Define one with [`tag!`](crate::tag). Because the name is part of the
16/// type, columns of `users.alias::<U>()` and `users.alias::<V>()` are
17/// different types and cannot be mixed up.
18pub trait Tag: 'static {
19    /// The SQL name.
20    const NAME: &'static str;
21}
22
23/// Defines a [`Tag`] type in one line.
24///
25/// # Examples
26///
27/// ```
28/// # use drizzle_core::tag;
29/// tag!(UsersAlias, "u");
30///
31/// let u = UsersAlias; // zero-sized, used as a type parameter
32/// assert_eq!(<UsersAlias as drizzle_core::Tag>::NAME, "u");
33/// ```
34///
35/// Visibility is supported:
36///
37/// ```
38/// # use drizzle_core::tag;
39/// tag!(pub MyTag, "my_tag");
40/// ```
41#[macro_export]
42macro_rules! tag {
43    ($vis:vis $name:ident, $sql_name:expr) => {
44        $vis struct $name;
45        impl $crate::Tag for $name {
46            const NAME: &'static str = $sql_name;
47        }
48    };
49}
50
51/// A value of type `T` labelled with the [`Tag`] `Name` at the type level.
52///
53/// Dereferences to `T`.
54#[derive(Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
55pub struct Tagged<T, Name: Tag> {
56    inner: T,
57    _tag: PhantomData<fn() -> Name>,
58}
59
60impl<T: Copy, Name: Tag> Copy for Tagged<T, Name> {}
61
62impl<T: Copy, Name: Tag> Clone for Tagged<T, Name> {
63    fn clone(&self) -> Self {
64        *self
65    }
66}
67
68impl<T, Name: Tag> Tagged<T, Name> {
69    /// Wraps `inner`.
70    pub const fn new(inner: T) -> Self {
71        Self {
72            inner,
73            _tag: PhantomData,
74        }
75    }
76
77    /// Returns the wrapped value.
78    pub fn into_inner(self) -> T {
79        self.inner
80    }
81}
82
83impl<T, Name: Tag> Deref for Tagged<T, Name> {
84    type Target = T;
85
86    fn deref(&self) -> &Self::Target {
87        &self.inner
88    }
89}
90
91/// A table's select, insert, update or partial model, generated by the
92/// table macros.
93#[diagnostic::on_unimplemented(
94    message = "`{Self}` is not a SQL model (Select, Insert, or Update)",
95    label = "this type cannot be used as a query model"
96)]
97pub trait SQLModel<'a, V: SQLParam>: ToSQL<'a, V> {
98    /// The columns this model sets or reads, in order.
99    ///
100    /// Models with a fixed column list return a borrowed slice; others may
101    /// allocate.
102    fn columns(&self) -> Cow<'static, [ColumnRef]>;
103    /// The model's values, comma-separated, in the order of
104    /// [`columns`](Self::columns).
105    fn values(&self) -> SQL<'a, V>;
106}
107
108/// A select model with a partial form, in which each field is optional.
109#[diagnostic::on_unimplemented(
110    message = "`{Self}` does not support partial field selection",
111    label = "this table's Select model does not implement SQLPartial"
112)]
113pub trait SQLPartial<'a, Value: SQLParam> {
114    /// The partial model: every field optional.
115    type Partial: SQLModel<'a, Value> + Default + 'a;
116
117    /// Returns an empty partial model.
118    #[must_use]
119    fn partial() -> Self::Partial {
120        Default::default()
121    }
122}
123
124/// A table for the dialect whose value type is `Value`.
125///
126/// Generated by `#[SQLiteTable]`, `#[PostgresTable]` and `#[MySQLTable]`,
127/// together with the table's models.
128#[diagnostic::on_unimplemented(
129    message = "`{Self}` is not a SQL table for this dialect",
130    label = "ensure this type was derived with #[SQLiteTable], #[PostgresTable], or #[MySQLTable]"
131)]
132pub trait SQLTable<'a, Type: SQLSchemaType, Value: SQLParam + 'a>:
133    SQLSchema<'a, Type, Value> + SQLTableInfo + Default + Clone + Copy
134{
135    /// The model a selected row decodes into (`SelectUsers`).
136    type Select: SQLModel<'a, Value> + SQLPartial<'a, Value> + 'a;
137    /// The table's foreign keys.
138    type ForeignKeys;
139    /// The table's primary key.
140    type PrimaryKey;
141    /// The table's other constraints.
142    type Constraints;
143
144    /// The model for inserting rows (`InsertUsers`). `T` tracks, at the
145    /// type level, which fields have been set.
146    type Insert<T>: SQLModel<'a, Value> + Default;
147
148    /// The model for updating rows (`UpdateUsers`).
149    type Update: SQLModel<'a, Value> + 'a;
150
151    /// This table under the alias `Name`, for self-joins and CTEs.
152    type Aliased<Name: Tag + 'static>: SQLTable<'a, Type, Value>;
153
154    /// Returns this table aliased as `Name::NAME`: `"users" AS "u"`.
155    fn alias<Name: Tag + 'static>() -> Self::Aliased<Name>;
156}
157
158/// A table's metadata as associated constants, generated by the table
159/// macros.
160///
161/// Every `DrizzleTable` also implements [`SQLTableInfo`].
162pub trait DrizzleTable: Send + Sync + 'static {
163    /// Unqualified table name.
164    const NAME: &'static str;
165
166    /// Table name with its schema, if any (`schema.table`).
167    const QUALIFIED_NAME: &'static str;
168
169    /// Schema namespace, if any.
170    const SCHEMA: Option<&'static str> = None;
171
172    /// Names of tables this table depends on via foreign keys.
173    const DEPENDENCY_NAMES: &'static [&'static str] = &[];
174
175    /// Full table metadata.
176    const TABLE_REF: TableRef;
177}
178
179// Every `DrizzleTable` is a `SQLTableInfo`.
180impl<T: DrizzleTable> SQLTableInfo for T {
181    fn name(&self) -> &'static str {
182        T::NAME
183    }
184
185    fn schema(&self) -> Option<&'static str> {
186        T::SCHEMA
187    }
188
189    fn qualified_name(&self) -> Cow<'static, str> {
190        Cow::Borrowed(T::QUALIFIED_NAME)
191    }
192}
193
194impl<'a, Type, Value, T> SQLTable<'a, Type, Value> for &T
195where
196    Type: SQLSchemaType,
197    Value: SQLParam + 'a,
198    T: SQLTable<'a, Type, Value>,
199    for<'r> &'r T: SQLSchema<'a, Type, Value> + SQLTableInfo + Default + Clone,
200{
201    type Select = T::Select;
202    type ForeignKeys = T::ForeignKeys;
203    type PrimaryKey = T::PrimaryKey;
204    type Constraints = T::Constraints;
205    type Insert<I> = T::Insert<I>;
206    type Update = T::Update;
207    type Aliased<Name: Tag + 'static> = T::Aliased<Name>;
208
209    fn alias<Name: Tag + 'static>() -> Self::Aliased<Name> {
210        T::alias::<Name>()
211    }
212}
213
214/// Runtime name information of a table, usable as a trait object.
215#[diagnostic::on_unimplemented(
216    message = "`{Self}` does not implement SQLTableInfo",
217    label = "ensure this type was derived with #[SQLiteTable], #[PostgresTable], or #[MySQLTable]"
218)]
219pub trait SQLTableInfo: Send + Sync {
220    /// Unqualified table name.
221    fn name(&self) -> &'static str;
222
223    /// Schema namespace for this table, if any.
224    fn schema(&self) -> Option<&'static str> {
225        None
226    }
227
228    /// Table name with its schema, if any (`schema.table`).
229    fn qualified_name(&self) -> Cow<'static, str> {
230        self.schema().map_or_else(
231            || Cow::Borrowed(self.name()),
232            |schema| Cow::Owned(format!("{schema}.{}", self.name())),
233        )
234    }
235}
236
237impl core::fmt::Debug for dyn SQLTableInfo {
238    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
239        f.debug_struct("SQLTableInfo")
240            .field("name", &self.name())
241            .field("schema", &self.schema())
242            .finish()
243    }
244}