1pub trait Relation<Target: ?Sized> {}
9
10pub trait Joinable<Target: ?Sized> {
18 fn fk_columns() -> &'static [(&'static str, &'static str)];
21}
22
23#[diagnostic::on_unimplemented(
31 message = "no single foreign key joins `{Self}` to `{From}`",
32 label = "a bare table joins on the one foreign key between the two tables",
33 note = "with no key, or several, name the condition: `.join((table, eq(left, right)))`"
34)]
35pub trait JoinKey<From: ?Sized, Via> {
36 fn pairs() -> &'static [(&'static str, &'static str)];
39
40 fn pair(pair: &(&'static str, &'static str)) -> (&'static str, &'static str);
42}
43
44#[derive(Debug, Clone, Copy)]
46pub struct JoinForward;
47
48#[derive(Debug, Clone, Copy)]
50pub struct JoinReverse;
51
52#[derive(Debug, Clone, Copy)]
54pub struct JoinExplicit;
55
56impl<Joined: Joinable<From>, From: ?Sized> JoinKey<From, JoinForward> for Joined {
57 fn pairs() -> &'static [(&'static str, &'static str)] {
58 Joined::fk_columns()
59 }
60
61 fn pair(pair: &(&'static str, &'static str)) -> (&'static str, &'static str) {
62 *pair
63 }
64}
65
66impl<Joined, From: Joinable<Joined> + ?Sized> JoinKey<From, JoinReverse> for Joined {
67 fn pairs() -> &'static [(&'static str, &'static str)] {
68 From::fk_columns()
69 }
70
71 fn pair(pair: &(&'static str, &'static str)) -> (&'static str, &'static str) {
72 (pair.1, pair.0)
73 }
74}
75
76#[diagnostic::on_unimplemented(
81 message = "table `{T}` is not part of schema `{Self}`",
82 label = "add this table as a field in your schema struct",
83 note = "all tables used in queries must be included in the schema"
84)]
85pub trait SchemaHasTable<T: ?Sized> {}
86
87#[cfg(feature = "query")]
96pub trait RelationDef: private::Sealed + 'static {
97 type Source;
99 type Target: crate::query::QueryTable;
101 type Card: CardWrap;
104 const NAME: &'static str;
106 fn fk_columns() -> &'static [(&'static str, &'static str)];
110
111 #[must_use]
116 fn junction() -> Option<JunctionMeta> {
117 None
118 }
119}
120
121#[cfg(feature = "query")]
124pub trait CardWrap: private::Sealed {
125 const CARDINALITY: crate::query::RelCardinality;
127 type Wrap<T>;
129
130 fn map_wrap<A, B>(data: Self::Wrap<A>, f: impl FnMut(A) -> B) -> Self::Wrap<B>;
132}
133
134#[cfg(feature = "query")]
139pub trait AssembleRel: RelationDef {
140 type Row<Inner, Child>;
142
143 fn assemble_row<Inner, Child>(
145 inner: Inner,
146 data: <Self::Card as CardWrap>::Wrap<Child>,
147 ) -> Self::Row<Inner, Child>;
148}
149
150#[cfg(feature = "query")]
153#[diagnostic::on_unimplemented(
154 message = "`.limit`, `.offset` and `.first` apply only to relations that load many rows",
155 label = "this relation loads at most one row",
156 note = "a LIMIT or OFFSET here could only drop or skip that row"
157)]
158pub trait Paginated: private::Sealed {}
159
160#[cfg(feature = "query")]
163#[diagnostic::on_unimplemented(
164 message = "a required relation cannot be filtered with `.r#where`",
165 label = "this relation always loads exactly one row",
166 note = "filter the root query instead, or make the foreign key an `Option` so the relation \
167 loads an `Option`"
168)]
169pub trait Filtered: private::Sealed {}
170
171#[cfg(feature = "query")]
173pub struct Many;
174
175#[cfg(feature = "query")]
177pub struct One;
178
179#[cfg(feature = "query")]
181pub struct OptionalOne;
182
183#[cfg(feature = "query")]
185#[derive(Debug, Clone, Copy)]
186pub struct JunctionMeta {
187 pub table: crate::TableSqlRef,
189 pub source_fk: &'static [(&'static str, &'static str)],
192 pub target_fk: &'static [(&'static str, &'static str)],
194}
195
196#[cfg(feature = "query")]
197impl private::Sealed for Many {}
198#[cfg(feature = "query")]
199impl private::Sealed for One {}
200#[cfg(feature = "query")]
201impl private::Sealed for OptionalOne {}
202
203#[cfg(feature = "query")]
204impl Paginated for Many {}
205#[cfg(feature = "query")]
206impl Filtered for Many {}
207#[cfg(feature = "query")]
208impl Filtered for OptionalOne {}
209
210#[cfg(feature = "query")]
211impl CardWrap for Many {
212 const CARDINALITY: crate::query::RelCardinality = crate::query::RelCardinality::Many;
213 type Wrap<T> = crate::prelude::Vec<T>;
214
215 fn map_wrap<A, B>(data: Self::Wrap<A>, f: impl FnMut(A) -> B) -> Self::Wrap<B> {
216 data.into_iter().map(f).collect()
217 }
218}
219
220#[cfg(feature = "query")]
221impl CardWrap for One {
222 const CARDINALITY: crate::query::RelCardinality = crate::query::RelCardinality::One;
223 type Wrap<T> = T;
224
225 fn map_wrap<A, B>(data: Self::Wrap<A>, mut f: impl FnMut(A) -> B) -> Self::Wrap<B> {
226 f(data)
227 }
228}
229
230#[cfg(feature = "query")]
231impl CardWrap for OptionalOne {
232 const CARDINALITY: crate::query::RelCardinality = crate::query::RelCardinality::OptionalOne;
233 type Wrap<T> = Option<T>;
234
235 fn map_wrap<A, B>(data: Self::Wrap<A>, f: impl FnMut(A) -> B) -> Self::Wrap<B> {
236 data.map(f)
237 }
238}
239
240#[cfg(feature = "query")]
241#[doc(hidden)]
242pub mod private {
243 pub trait Sealed {}
244}