use crate::prelude::*;
use crate::{ColumnRef, SQL, SQLParam, SQLSchema, SQLSchemaType, TableRef, ToSQL};
use core::marker::PhantomData;
use core::ops::Deref;
pub struct Empty;
pub struct NonEmpty;
pub trait Tag {
const NAME: &'static str;
}
#[macro_export]
macro_rules! tag {
($vis:vis $name:ident, $sql_name:expr) => {
$vis struct $name;
impl $crate::Tag for $name {
const NAME: &'static str = $sql_name;
}
};
}
#[derive(Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Tagged<T, Name: Tag> {
inner: T,
_tag: PhantomData<fn() -> Name>,
}
impl<T: Copy, Name: Tag> Copy for Tagged<T, Name> {}
impl<T: Copy, Name: Tag> Clone for Tagged<T, Name> {
fn clone(&self) -> Self {
*self
}
}
impl<T, Name: Tag> Tagged<T, Name> {
pub const fn new(inner: T) -> Self {
Self {
inner,
_tag: PhantomData,
}
}
pub fn into_inner(self) -> T {
self.inner
}
}
impl<T, Name: Tag> Deref for Tagged<T, Name> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a SQL model (Select, Insert, or Update)",
label = "this type cannot be used as a query model"
)]
pub trait SQLModel<'a, V: SQLParam>: ToSQL<'a, V> {
fn columns(&self) -> Cow<'static, [ColumnRef]>;
fn values(&self) -> SQL<'a, V>;
}
#[diagnostic::on_unimplemented(
message = "`{Self}` does not support partial field selection",
label = "this table's Select model does not implement SQLPartial"
)]
pub trait SQLPartial<'a, Value: SQLParam> {
type Partial: SQLModel<'a, Value> + Default + 'a;
#[must_use]
fn partial() -> Self::Partial {
Default::default()
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a SQL table for this dialect",
label = "ensure this type was derived with #[SQLiteTable] or #[PostgresTable]"
)]
pub trait SQLTable<'a, Type: SQLSchemaType, Value: SQLParam + 'a>:
SQLSchema<'a, Type, Value> + SQLTableInfo + Default + Clone + Copy
{
type Select: SQLModel<'a, Value> + SQLPartial<'a, Value> + Default + 'a;
type ForeignKeys;
type PrimaryKey;
type Constraints;
type Insert<T>: SQLModel<'a, Value> + Default;
type Update: SQLModel<'a, Value> + 'a;
type Aliased<Name: Tag + 'static>: SQLTable<'a, Type, Value>;
fn alias<Name: Tag + 'static>() -> Self::Aliased<Name>;
}
pub trait DrizzleTable: Send + Sync + 'static {
const NAME: &'static str;
const QUALIFIED_NAME: &'static str;
const SCHEMA: Option<&'static str> = None;
const DEPENDENCY_NAMES: &'static [&'static str] = &[];
const TABLE_REF: TableRef;
}
impl<T: DrizzleTable> SQLTableInfo for T {
fn name(&self) -> &'static str {
T::NAME
}
fn schema(&self) -> Option<&'static str> {
T::SCHEMA
}
fn qualified_name(&self) -> Cow<'static, str> {
Cow::Borrowed(T::QUALIFIED_NAME)
}
}
impl<'a, Type, Value, T> SQLTable<'a, Type, Value> for &T
where
Type: SQLSchemaType,
Value: SQLParam + 'a,
T: SQLTable<'a, Type, Value>,
for<'r> &'r T: SQLSchema<'a, Type, Value> + SQLTableInfo + Default + Clone,
{
type Select = T::Select;
type ForeignKeys = T::ForeignKeys;
type PrimaryKey = T::PrimaryKey;
type Constraints = T::Constraints;
type Insert<I> = T::Insert<I>;
type Update = T::Update;
type Aliased<Name: Tag + 'static> = T::Aliased<Name>;
fn alias<Name: Tag + 'static>() -> Self::Aliased<Name> {
T::alias::<Name>()
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` does not implement SQLTableInfo",
label = "ensure this type was derived with #[SQLiteTable] or #[PostgresTable]"
)]
pub trait SQLTableInfo: Send + Sync {
fn name(&self) -> &'static str;
fn schema(&self) -> Option<&'static str> {
None
}
fn qualified_name(&self) -> Cow<'static, str> {
self.schema().map_or_else(
|| Cow::Borrowed(self.name()),
|schema| Cow::Owned(format!("{schema}.{}", self.name())),
)
}
}
impl core::fmt::Debug for dyn SQLTableInfo {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SQLTableInfo")
.field("name", &self.name())
.field("schema", &self.schema())
.finish()
}
}