lsor_core/
sort.rs

1use async_graphql::Enum;
2use chrono::{DateTime, Utc};
3use uuid::Uuid;
4
5use crate::{
6    cursor::{Cursor, Iterable},
7    driver::{Driver, PushPrql},
8    take::Taken,
9};
10
11/// The implementation of `PushPrql` must only push the expression that is being
12/// ordered by. It must not push the order itself.
13pub trait Sorting: PushPrql {
14    fn order(&self) -> Order;
15    fn flip(&self) -> impl Sorting;
16    fn push_to_driver_with_order(&self, driver: &mut Driver);
17}
18
19impl<T> Sorting for &T
20where
21    T: Sorting,
22{
23    fn order(&self) -> Order {
24        (*self).order()
25    }
26
27    fn flip(&self) -> impl Sorting {
28        (*self).flip()
29    }
30
31    fn push_to_driver_with_order(&self, driver: &mut Driver) {
32        (*self).push_to_driver_with_order(driver)
33    }
34}
35
36pub trait SortedBy {
37    fn sorting(&self) -> impl Sorting;
38}
39
40impl<T> SortedBy for &T
41where
42    T: SortedBy,
43{
44    fn sorting(&self) -> impl Sorting {
45        (*self).sorting()
46    }
47}
48
49#[derive(Clone, Copy, Debug, Enum, Eq, PartialEq)]
50pub enum Order {
51    Asc,
52    Desc,
53}
54
55impl Order {
56    pub fn flip(&self) -> Self {
57        match self {
58            Self::Asc => Self::Desc,
59            Self::Desc => Self::Asc,
60        }
61    }
62
63    pub fn is_asc(&self) -> bool {
64        matches!(self, Self::Asc)
65    }
66
67    pub fn is_desc(&self) -> bool {
68        matches!(self, Self::Desc)
69    }
70}
71
72impl PushPrql for Order {
73    fn push_to_driver(&self, driver: &mut Driver) {
74        if let Self::Desc = self {
75            driver.push('-');
76        }
77    }
78}
79
80pub struct Sort<By> {
81    pub order: Order,
82    pub by: By,
83}
84
85impl<By> Sorting for Sort<By>
86where
87    By: PushPrql,
88{
89    fn order(&self) -> Order {
90        self.order
91    }
92
93    fn flip(&self) -> impl Sorting {
94        Sort {
95            order: self.order.flip(),
96            by: &self.by,
97        }
98    }
99
100    fn push_to_driver_with_order(&self, driver: &mut Driver) {
101        self.order.push_to_driver(driver);
102        self.by.push_to_driver(driver);
103    }
104}
105
106impl<By> PushPrql for Sort<By>
107where
108    By: PushPrql,
109{
110    fn push_to_driver(&self, driver: &mut Driver) {
111        self.by.push_to_driver(driver);
112    }
113}
114
115pub struct Sorted<Query, Sort> {
116    pub query: Query,
117    pub sort: Sort,
118}
119
120impl<Query, Sort> Sorted<Query, Sort> {
121    pub fn take(&self, n: usize) -> Taken<&Self> {
122        Taken { query: self, n }
123    }
124}
125
126impl<Query, Sort> SortedBy for Sorted<Query, Sort>
127where
128    Sort: Sorting,
129{
130    fn sorting(&self) -> impl Sorting {
131        &self.sort
132    }
133}
134
135impl<Query, Sort> PushPrql for Sorted<Query, Sort>
136where
137    Query: PushPrql,
138    Sort: Sorting,
139{
140    fn push_to_driver(&self, driver: &mut Driver) {
141        self.query.push_to_driver(driver);
142        driver.push("\nsort { ");
143        self.sort.push_to_driver_with_order(driver);
144        driver.push(" }");
145    }
146}
147
148pub trait SortBy<By> {
149    fn sort_by(by: By) -> Sort<By>;
150}
151
152impl<By> SortBy<By> for i32
153where
154    By: PushPrql,
155{
156    fn sort_by(by: By) -> Sort<By> {
157        Sort {
158            order: Order::Asc,
159            by,
160        }
161    }
162}
163
164impl<By> SortBy<By> for u64
165where
166    By: PushPrql,
167{
168    fn sort_by(by: By) -> Sort<By> {
169        Sort {
170            order: Order::Asc,
171            by,
172        }
173    }
174}
175
176impl<By> SortBy<By> for String
177where
178    By: PushPrql,
179{
180    fn sort_by(by: By) -> Sort<By> {
181        Sort {
182            order: Order::Asc,
183            by,
184        }
185    }
186}
187
188macro_rules! impl_sortable {
189    ($t:ty, $i:ident, $c:expr) => {
190        impl Sortable for $t {
191            type Sort = $i;
192        }
193
194        impl Sortable for Option<$t> {
195            type Sort = $i;
196        }
197
198        #[derive(Clone, Copy, Debug, Enum, Eq, PartialEq)]
199        #[graphql(rename_items = "snake_case")]
200        pub enum $i {
201            Asc,
202            Desc,
203        }
204
205        impl Iterable for $i {
206            fn cursor(&self) -> Cursor {
207                $c
208            }
209        }
210
211        impl $i {
212            pub fn order(&self) -> Order {
213                match self {
214                    Self::Asc => Order::Desc,
215                    Self::Desc => Order::Asc,
216                }
217            }
218
219            pub fn flip_as_self(&self) -> Self {
220                match self {
221                    Self::Asc => Self::Desc,
222                    Self::Desc => Self::Asc,
223                }
224            }
225
226            pub fn push_to_driver_with_lhs(&self, lhs: &dyn PushPrql, driver: &mut Driver) {
227                lhs.push_to_driver(driver);
228            }
229
230            pub fn push_to_driver_with_order_with_lhs(
231                &self,
232                lhs: &dyn PushPrql,
233                driver: &mut Driver,
234            ) {
235                match self {
236                    Self::Asc => {
237                        lhs.push_to_driver(driver);
238                    }
239                    Self::Desc => {
240                        driver.push('-');
241                        lhs.push_to_driver(driver);
242                    }
243                }
244            }
245        }
246    };
247}
248
249pub trait Sortable {
250    type Sort;
251}
252
253impl_sortable!(i32, I32Sort, Cursor::I32);
254impl_sortable!(i64, I64Sort, Cursor::I64);
255impl_sortable!(u32, U32Sort, Cursor::I32);
256impl_sortable!(u64, U64Sort, Cursor::I64);
257impl_sortable!(String, StringSort, Cursor::String);
258impl_sortable!(Uuid, UuidSort, Cursor::Uuid);
259impl_sortable!(DateTime<Utc>, DateTimeSort, Cursor::DateTime);
260
261#[cfg(test)]
262mod test {
263    use crate::{
264        column::{col, json},
265        cond::gt,
266        from::from,
267        table::table,
268    };
269
270    use super::*;
271
272    #[test]
273    fn test_sort() {
274        let mut driver = Driver::new();
275        {
276            from(table("users"))
277                .sort(col("age").asc())
278                .push_to_driver(&mut driver);
279        }
280        assert_eq!(driver.sql(), "SELECT * FROM users ORDER BY age");
281    }
282
283    #[test]
284    fn test_sort_desc() {
285        let mut driver = Driver::new();
286        {
287            from(table("users"))
288                .sort(col("age").desc())
289                .push_to_driver(&mut driver);
290        }
291        dbg!(driver.prql());
292        assert_eq!(driver.sql(), "SELECT * FROM users ORDER BY age DESC");
293    }
294
295    #[test]
296    fn test_filter_sort() {
297        let mut driver = Driver::new();
298        {
299            from(table("users"))
300                .filter(gt(col("age"), 18))
301                .sort(col("age").asc())
302                .push_to_driver(&mut driver);
303        }
304        assert_eq!(
305            driver.sql(),
306            "SELECT * FROM users WHERE age > $1 ORDER BY age"
307        );
308    }
309
310    #[test]
311    fn test_take_sort() {
312        let mut driver = Driver::new();
313        {
314            from(table("users"))
315                .take(10)
316                .sort(col("age").asc())
317                .push_to_driver(&mut driver);
318        }
319        assert_eq!(
320            driver.sql(),
321            "WITH table_0 AS (SELECT * FROM users LIMIT 10) SELECT * FROM table_0 ORDER BY age"
322        );
323    }
324
325    #[test]
326    fn test_sort_json() {
327        let mut driver = Driver::new();
328        {
329            from(table("users"))
330                .sort(json(col("info")).get("age").asc())
331                .push_to_driver(&mut driver);
332        }
333        assert_eq!(driver.sql(), "WITH table_0 AS (SELECT *, info->'age' AS _expr_0 FROM users) SELECT * FROM table_0 ORDER BY _expr_0");
334
335        let mut driver = Driver::new();
336        {
337            from(table("users"))
338                .sort(json(col("info")).get("age").desc())
339                .push_to_driver(&mut driver);
340        }
341        assert_eq!(driver.sql(), "WITH table_0 AS (SELECT *, info->'age' AS _expr_0 FROM users) SELECT * FROM table_0 ORDER BY _expr_0 DESC");
342    }
343}