pub trait Relation<Target: ?Sized> {}
pub trait Joinable<Target: ?Sized> {
fn fk_columns() -> &'static [(&'static str, &'static str)];
}
#[diagnostic::on_unimplemented(
message = "no single foreign key joins `{Self}` to `{From}`",
label = "a bare table joins on the one foreign key between the two tables",
note = "with no key, or several, name the condition: `.join((table, eq(left, right)))`"
)]
pub trait JoinKey<From: ?Sized, Via> {
fn pairs() -> &'static [(&'static str, &'static str)];
fn pair(pair: &(&'static str, &'static str)) -> (&'static str, &'static str);
}
#[derive(Debug, Clone, Copy)]
pub struct JoinForward;
#[derive(Debug, Clone, Copy)]
pub struct JoinReverse;
#[derive(Debug, Clone, Copy)]
pub struct JoinExplicit;
impl<Joined: Joinable<From>, From: ?Sized> JoinKey<From, JoinForward> for Joined {
fn pairs() -> &'static [(&'static str, &'static str)] {
Joined::fk_columns()
}
fn pair(pair: &(&'static str, &'static str)) -> (&'static str, &'static str) {
*pair
}
}
impl<Joined, From: Joinable<Joined> + ?Sized> JoinKey<From, JoinReverse> for Joined {
fn pairs() -> &'static [(&'static str, &'static str)] {
From::fk_columns()
}
fn pair(pair: &(&'static str, &'static str)) -> (&'static str, &'static str) {
(pair.1, pair.0)
}
}
#[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> {}
#[cfg(feature = "query")]
pub trait RelationDef: private::Sealed + 'static {
type Source;
type Target: crate::query::QueryTable;
type Card: CardWrap;
const NAME: &'static str;
fn fk_columns() -> &'static [(&'static str, &'static str)];
#[must_use]
fn junction() -> Option<JunctionMeta> {
None
}
}
#[cfg(feature = "query")]
pub trait CardWrap: private::Sealed {
const CARDINALITY: crate::query::RelCardinality;
type Wrap<T>;
fn map_wrap<A, B>(data: Self::Wrap<A>, f: impl FnMut(A) -> B) -> Self::Wrap<B>;
}
#[cfg(feature = "query")]
pub trait AssembleRel: RelationDef {
type Row<Inner, Child>;
fn assemble_row<Inner, Child>(
inner: Inner,
data: <Self::Card as CardWrap>::Wrap<Child>,
) -> Self::Row<Inner, Child>;
}
#[cfg(feature = "query")]
#[diagnostic::on_unimplemented(
message = "`.limit`, `.offset` and `.first` apply only to relations that load many rows",
label = "this relation loads at most one row",
note = "a LIMIT or OFFSET here could only drop or skip that row"
)]
pub trait Paginated: private::Sealed {}
#[cfg(feature = "query")]
#[diagnostic::on_unimplemented(
message = "a required relation cannot be filtered with `.r#where`",
label = "this relation always loads exactly one row",
note = "filter the root query instead, or make the foreign key an `Option` so the relation \
loads an `Option`"
)]
pub trait Filtered: private::Sealed {}
#[cfg(feature = "query")]
pub struct Many;
#[cfg(feature = "query")]
pub struct One;
#[cfg(feature = "query")]
pub struct OptionalOne;
#[cfg(feature = "query")]
#[derive(Debug, Clone, Copy)]
pub struct JunctionMeta {
pub table: crate::TableSqlRef,
pub source_fk: &'static [(&'static str, &'static str)],
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 Paginated for Many {}
#[cfg(feature = "query")]
impl Filtered for Many {}
#[cfg(feature = "query")]
impl Filtered 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 {}
}