Skip to main content

drizzle_core/traits/
mod.rs

1//! Traits for schema items (tables, columns, indexes, views, ...) and SQL
2//! rendering.
3//!
4//! The table, index, view and schema macros implement almost all of these.
5//! Users mostly call their methods, or meet them in error messages.
6
7use crate::TableRef;
8use crate::prelude::*;
9use core::any::Any;
10
11#[macro_use]
12mod tuple;
13
14mod column;
15mod constraint;
16mod foreign_key;
17mod index;
18mod param;
19mod policy;
20mod table;
21mod to_sql;
22mod type_set;
23mod view;
24
25pub use column::*;
26pub use constraint::*;
27pub use foreign_key::*;
28pub use index::*;
29pub use param::*;
30pub use policy::*;
31pub use table::*;
32pub use to_sql::*;
33pub use type_set::*;
34pub use view::*;
35
36/// A schema item (table, column, index, view, enum) with a name and its
37/// `CREATE` SQL.
38///
39/// Generated by the schema macros. `'a` is the lifetime of borrowed
40/// parameter values in the SQL it renders.
41#[diagnostic::on_unimplemented(
42    message = "`{Self}` is not a SQL schema element",
43    label = "this type was not derived with a drizzle schema macro"
44)]
45pub trait SQLSchema<'a, T, V: SQLParam + 'a>: ToSQL<'a, V> {
46    /// The item's SQL name.
47    const NAME: &'static str;
48    /// The kind of item (a dialect-specific enum, or the column's SQL type
49    /// name for columns).
50    const TYPE: T;
51    /// The statement that creates the item (`CREATE TABLE ...`), or an
52    /// empty string when it has none.
53    const SQL: &'static str;
54    /// The scope the item's name must be unique in, and the name: a schema
55    /// for a `PostgreSQL` table, or a table for a `MySQL` index. The schema
56    /// macros reject two items with one scope and name at compile time.
57    /// `None` for items that claim no name.
58    #[doc(hidden)]
59    const NAME_SCOPE: Option<(&'static str, &'static str)> = None;
60}
61
62impl<'a, S, T, V> SQLSchema<'a, T, V> for &S
63where
64    S: SQLSchema<'a, T, V>,
65    V: SQLParam + 'a,
66{
67    const NAME: &'static str = <S as SQLSchema<'a, T, V>>::NAME;
68    const TYPE: T = <S as SQLSchema<'a, T, V>>::TYPE;
69    const SQL: &'static str = <S as SQLSchema<'a, T, V>>::SQL;
70    const NAME_SCOPE: Option<(&'static str, &'static str)> = <S as SQLSchema<'a, T, V>>::NAME_SCOPE;
71}
72
73/// The index of the first item whose [`SQLSchema::NAME_SCOPE`] an earlier
74/// item already has, for the schema macros' compile-time check.
75#[doc(hidden)]
76#[must_use]
77pub const fn first_duplicate_name(names: &[Option<(&str, &str)>]) -> Option<usize> {
78    const fn same(a: &str, b: &str) -> bool {
79        let (a, b) = (a.as_bytes(), b.as_bytes());
80        if a.len() != b.len() {
81            return false;
82        }
83        let mut i = 0;
84        while i < a.len() {
85            if a[i] != b[i] {
86                return false;
87            }
88            i += 1;
89        }
90        true
91    }
92    let mut later = 1;
93    while later < names.len() {
94        if let Some((scope, name)) = names[later] {
95            let mut earlier = 0;
96            while earlier < later {
97                if let Some((other_scope, other_name)) = names[earlier]
98                    && same(scope, other_scope)
99                    && same(name, other_name)
100                {
101                    return Some(later);
102                }
103                earlier += 1;
104            }
105        }
106        later += 1;
107    }
108    None
109}
110
111/// The tables a schema item adds to its schema, as a type-level list.
112///
113/// A table adds itself (`Cons<Table, Nil>`); indexes, views and enums add
114/// nothing (`Nil`).
115#[diagnostic::on_unimplemented(
116    message = "`{Self}` is not a recognized schema item",
117    label = "schema items must be tables, views, indexes, or enums derived with drizzle macros"
118)]
119pub trait SchemaItemTables {
120    /// `Cons<Table, Nil>` for a table, `Nil` otherwise.
121    type Tables: TypeSet;
122    /// The table's metadata, or `None` for an item that is not a table.
123    const TABLE_REF_CONST: Option<&'static TableRef> = None;
124}
125
126/// Dialect marker for schema items: `SQLiteSchemaType`,
127/// `PostgresSchemaType` or `MySQLSchemaType`.
128///
129/// Keeps a SQLite table from being used with a PostgreSQL schema, and so on.
130#[diagnostic::on_unimplemented(
131    message = "`{Self}` is not a SQL schema type marker",
132    label = "expected a dialect marker like SQLiteSchemaType, PostgresSchemaType, or MySQLSchemaType"
133)]
134pub trait SQLSchemaType: core::fmt::Debug + Any + Send + Sync {}
135
136/// A schema struct (generated by `#[SQLiteSchema]`, `#[PostgresSchema]`,
137/// ...) that can list its tables and render its `CREATE` statements.
138pub trait SQLSchemaImpl: Any + Send + Sync {
139    /// Metadata of every table in the schema.
140    fn table_refs(&self) -> &'static [&'static TableRef];
141    /// Returns the `CREATE` statements for the schema's items.
142    ///
143    /// # Errors
144    ///
145    /// Returns an error if SQL generation for any table fails.
146    fn create_statements(&self) -> crate::error::Result<impl Iterator<Item = String>>;
147}