1use std::marker::PhantomData;
4
5use crate::Model;
6use crate::fields::ForeignKey;
7use crate::query::{SqlValue, ToSqlValue};
8
9pub struct Column<M, T> {
14 name: &'static str,
15 _marker: PhantomData<fn() -> (M, T)>,
16}
17
18impl<M, T> Copy for Column<M, T> {}
19
20impl<M, T> Clone for Column<M, T> {
21 fn clone(&self) -> Self {
22 *self
23 }
24}
25
26impl<M, T> std::fmt::Debug for Column<M, T> {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.write_fmt(format_args!("Column({:?})", self.name))
29 }
30}
31
32impl<M, T> Column<M, T> {
33 pub const fn new(name: &'static str) -> Self {
35 Self {
36 name,
37 _marker: PhantomData,
38 }
39 }
40
41 pub const fn name(&self) -> &'static str {
43 self.name
44 }
45
46 pub const fn asc(self) -> Ordering<M> {
48 Ordering::new(self.name, false)
49 }
50
51 pub const fn desc(self) -> Ordering<M> {
53 Ordering::new(self.name, true)
54 }
55}
56
57pub struct Ordering<M> {
59 name: &'static str,
60 descending: bool,
61 _model: PhantomData<fn() -> M>,
62}
63
64impl<M> Ordering<M> {
65 const fn new(name: &'static str, descending: bool) -> Self {
66 Self {
67 name,
68 descending,
69 _model: PhantomData,
70 }
71 }
72}
73
74impl<M> Copy for Ordering<M> {}
75
76impl<M> Clone for Ordering<M> {
77 fn clone(&self) -> Self {
78 *self
79 }
80}
81
82impl<M> std::fmt::Debug for Ordering<M> {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 f.write_fmt(format_args!(
85 "Ordering({:?}, descending={})",
86 self.name, self.descending
87 ))
88 }
89}
90
91pub trait ColumnRef<M> {
95 fn column_name(&self) -> &str;
96}
97
98impl<M, T> ColumnRef<M> for Column<M, T> {
99 fn column_name(&self) -> &str {
100 self.name
101 }
102}
103
104impl<M> ColumnRef<M> for &str {
105 fn column_name(&self) -> &str {
106 self
107 }
108}
109
110pub trait ForeignKeyRef<M, T>: ColumnRef<M> {}
114
115impl<M, T: Model> ForeignKeyRef<M, T> for Column<M, ForeignKey<T>> {}
116
117impl<M, T> ForeignKeyRef<M, T> for &str {}
118
119pub struct ManyToManyField<M, R>(PhantomData<fn() -> (M, R)>);
122
123impl<M, R> ManyToManyField<M, R> {
124 pub const fn new() -> Self {
126 Self(PhantomData)
127 }
128}
129
130impl<M, R> Default for ManyToManyField<M, R> {
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub enum PrefetchKind<'a> {
139 ReverseFk(&'a str),
141 ManyToMany,
143}
144
145pub trait PrefetchRef<M, R> {
149 fn prefetch_kind(&self) -> PrefetchKind<'_>;
150}
151
152impl<M: Model, R> PrefetchRef<M, R> for Column<R, ForeignKey<M>> {
153 fn prefetch_kind(&self) -> PrefetchKind<'_> {
154 PrefetchKind::ReverseFk(self.name)
155 }
156}
157
158impl<M, R> PrefetchRef<M, R> for &str {
159 fn prefetch_kind(&self) -> PrefetchKind<'_> {
160 PrefetchKind::ReverseFk(self)
161 }
162}
163
164impl<M, R> PrefetchRef<M, R> for ManyToManyField<M, R> {
165 fn prefetch_kind(&self) -> PrefetchKind<'_> {
166 PrefetchKind::ManyToMany
167 }
168}
169
170pub trait OrderRef<M> {
173 fn order_parts(&self) -> (&str, bool);
174}
175
176impl<M, T> OrderRef<M> for Column<M, T> {
177 fn order_parts(&self) -> (&str, bool) {
178 (self.name, false)
179 }
180}
181
182impl<M> OrderRef<M> for Ordering<M> {
183 fn order_parts(&self) -> (&str, bool) {
184 (self.name, self.descending)
185 }
186}
187
188impl<M> OrderRef<M> for &str {
189 fn order_parts(&self) -> (&str, bool) {
190 match self.strip_prefix("-") {
191 Some(name) => (name, true),
192 None => (*self, false),
193 }
194 }
195}
196
197#[diagnostic::on_unimplemented(
202 message = "`{Self}` cannot be compared to a `{T}` column",
203 label = "expected a `{T}` value or its plain Rust equivalent"
204)]
205pub trait FilterValue<T> {
206 fn into_sql_value(self) -> SqlValue;
207}
208
209#[diagnostic::do_not_recommend]
210impl<T: ToSqlValue> FilterValue<T> for T {
211 fn into_sql_value(self) -> SqlValue {
212 self.to_sql_value()
213 }
214}
215
216#[diagnostic::on_unimplemented(
221 message = "`{Self}` is not a column of this model that accepts a `{V}` value",
222 note = "pass a typed column such as `Model::NAME` with a value of its field type, or a column name as `&str`"
223)]
224pub trait FilterColumn<M, V>: ColumnRef<M> {
225 fn bind(&self, val: V) -> SqlValue;
226}
227
228#[diagnostic::do_not_recommend]
229impl<M, T, V: FilterValue<T>> FilterColumn<M, V> for Column<M, T> {
230 fn bind(&self, val: V) -> SqlValue {
231 val.into_sql_value()
232 }
233}
234
235#[diagnostic::do_not_recommend]
236impl<M, V: Into<SqlValue>> FilterColumn<M, V> for &str {
237 fn bind(&self, val: V) -> SqlValue {
238 val.into()
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use crate::fields::{FieldEmail, FieldInt, FieldText};
246
247 struct User;
248
249 #[test]
250 fn filter_values_are_typed() {
251 let age = Column::<User, FieldInt>::new("age");
252 assert_eq!(age.bind(18), SqlValue::Int(18));
253 assert_eq!(age.bind(FieldInt(18)), SqlValue::Int(18));
254 let email = Column::<User, FieldEmail>::new("email");
255 assert_eq!(email.bind("a@b.c"), SqlValue::Text("a@b.c".into()));
256 assert_eq!(
257 FilterColumn::<User, _>::bind(&"age", 18i64),
258 SqlValue::BigInt(18)
259 );
260 }
261
262 #[test]
263 fn column_name() {
264 assert_eq!(
265 Column::<User, FieldText>::new("email").column_name(),
266 "email"
267 );
268 assert_eq!(ColumnRef::<User>::column_name(&"email"), "email");
269 }
270
271 #[test]
272 fn order_parts() {
273 let column = Column::<User, FieldText>::new("name");
274 assert_eq!(column.order_parts(), ("name", false));
275 assert_eq!(column.desc().order_parts(), ("name", true));
276 assert_eq!(column.asc().order_parts(), ("name", false));
277 assert_eq!(OrderRef::<User>::order_parts(&"-name"), ("name", true));
278 assert_eq!(OrderRef::<User>::order_parts(&"name"), ("name", false));
279 }
280
281 fn assert_traits<T: Send + Sync + Copy>() {}
282
283 #[test]
284 fn column_is_send_sync_copy() {
285 assert_traits::<Column<*const u8, *const u8>>();
286 }
287}