drizzle-core 0.3.1

A type-safe SQL query builder for Rust
Documentation
//! Traits that describe foreign-key relations between tables, generated by
//! the table and schema macros.

/// The table `Self` has a foreign key to `Target`.
///
/// Generated by the table macros for each declared foreign key.
pub trait Relation<Target: ?Sized> {}

/// `Self` can be joined to `Target` without an explicit ON condition.
///
/// Generated by the table macros when there is exactly one foreign key
/// between the two tables. It lets `.join(table)` derive the ON condition
/// from the key. With several foreign keys to the same table, pass an
/// explicit condition: `.join((table, condition))`.
pub trait Joinable<Target: ?Sized> {
    /// Returns the `(self_column, target_column)` pairs the ON condition
    /// matches.
    fn fk_columns() -> &'static [(&'static str, &'static str)];
}

/// The table `T` is part of the schema `Self`.
///
/// Generated by the schema macros. Query builders require it, so using a
/// table that is missing from the schema struct is a compile error.
#[diagnostic::on_unimplemented(
    message = "table `{T}` is not part of schema `{Self}`",
    label = "add this table as a field in your schema struct",
    note = "all tables used in queries must be included in the schema"
)]
pub trait SchemaHasTable<T: ?Sized> {}

// =============================================================================
// Query API: RelationDef and Cardinality
// =============================================================================

/// A relation between two tables for the relational query API (`db.query`).
///
/// Generated by the schema macros for each relation; not implemented by
/// hand.
#[cfg(feature = "query")]
pub trait RelationDef: private::Sealed + 'static {
    /// The table the relation starts from.
    type Source;
    /// The table the relation loads.
    type Target: crate::query::QueryTable;
    /// How many target rows each source row has: [`Many`], [`One`] or
    /// [`OptionalOne`].
    type Card: CardWrap;
    /// The relation's name, used as its JSON key.
    const NAME: &'static str;
    /// Column pairs for the join condition.
    ///
    /// Each pair `(a, b)` renders `target_alias."a" = parent_alias."b"`.
    fn fk_columns() -> &'static [(&'static str, &'static str)];

    /// Junction table metadata for many-to-many relations.
    ///
    /// `None` for a direct foreign key. When `Some`, the generated SQL joins
    /// through the junction table instead.
    #[must_use]
    fn junction() -> Option<JunctionMeta> {
        None
    }
}

/// Maps a cardinality marker to the type that holds loaded rows: `Vec<T>`,
/// `T` or `Option<T>`.
#[cfg(feature = "query")]
pub trait CardWrap: private::Sealed {
    /// The same cardinality, as a runtime value.
    const CARDINALITY: crate::query::RelCardinality;
    /// The container for loaded rows of type `T`.
    type Wrap<T>;

    /// Maps the wrapped value from `A` to `B`.
    fn map_wrap<A, B>(data: Self::Wrap<A>, f: impl FnMut(A) -> B) -> Self::Wrap<B>;
}

/// Builds the row type that holds a loaded relation.
///
/// Generated for each relation. `Row<Inner, Child>` is a generated struct
/// such as `UsersWithPosts<Inner, Child>`.
#[cfg(feature = "query")]
pub trait AssembleRel: RelationDef {
    /// Row type produced when this relation is included in a query.
    type Row<Inner, Child>;

    /// Builds a with-row from the composed inner row and this relation's data.
    fn assemble_row<Inner, Child>(
        inner: Inner,
        data: <Self::Card as CardWrap>::Wrap<Child>,
    ) -> Self::Row<Inner, Child>;
}

/// Cardinality marker: any number of rows, loaded as `Vec<T>`.
#[cfg(feature = "query")]
pub struct Many;

/// Cardinality marker: exactly one row, loaded as `T`.
#[cfg(feature = "query")]
pub struct One;

/// Cardinality marker: zero or one row, loaded as `Option<T>`.
#[cfg(feature = "query")]
pub struct OptionalOne;

/// Metadata for many-to-many relations through a junction table.
#[cfg(feature = "query")]
#[derive(Debug, Clone, Copy)]
pub struct JunctionMeta {
    /// The junction table.
    pub table: crate::TableSqlRef,
    /// `(junction_col, source_col)` pairs that tie a junction row to the
    /// parent row.
    pub source_fk: &'static [(&'static str, &'static str)],
    /// `(junction_col, target_col)` pairs used to join the target table.
    pub target_fk: &'static [(&'static str, &'static str)],
}

#[cfg(feature = "query")]
impl private::Sealed for Many {}
#[cfg(feature = "query")]
impl private::Sealed for One {}
#[cfg(feature = "query")]
impl private::Sealed for OptionalOne {}

#[cfg(feature = "query")]
impl CardWrap for Many {
    const CARDINALITY: crate::query::RelCardinality = crate::query::RelCardinality::Many;
    type Wrap<T> = crate::prelude::Vec<T>;

    fn map_wrap<A, B>(data: Self::Wrap<A>, f: impl FnMut(A) -> B) -> Self::Wrap<B> {
        data.into_iter().map(f).collect()
    }
}

#[cfg(feature = "query")]
impl CardWrap for One {
    const CARDINALITY: crate::query::RelCardinality = crate::query::RelCardinality::One;
    type Wrap<T> = T;

    fn map_wrap<A, B>(data: Self::Wrap<A>, mut f: impl FnMut(A) -> B) -> Self::Wrap<B> {
        f(data)
    }
}

#[cfg(feature = "query")]
impl CardWrap for OptionalOne {
    const CARDINALITY: crate::query::RelCardinality = crate::query::RelCardinality::OptionalOne;
    type Wrap<T> = Option<T>;

    fn map_wrap<A, B>(data: Self::Wrap<A>, f: impl FnMut(A) -> B) -> Self::Wrap<B> {
        data.map(f)
    }
}

#[cfg(feature = "query")]
#[doc(hidden)]
pub mod private {
    pub trait Sealed {}
}