1use serde::{
2 Deserialize,
3 Serialize,
4};
5use sqlxo_traits::{
6 QueryContext,
7 WebJoinPayload,
8 WebLeaf,
9 WebQueryModel,
10 WebSortField,
11};
12use utoipa::{
13 IntoParams,
14 ToSchema,
15};
16
17use crate::select::HavingPredicate;
18
19mod builder;
20mod page;
21pub use page::{
22 WebPage,
23 WebPagination,
24};
25
26#[derive(Clone, Serialize, Deserialize, ToSchema, Debug, IntoParams)]
27#[serde(bound(deserialize = "Q: WebLeaf + Deserialize<'de>, S: \
28 WebSortField + Deserialize<'de>, A: WebLeaf + \
29 Deserialize<'de>, J: WebJoinPayload + \
30 Deserialize<'de>"))]
31#[into_params(parameter_in = Query)]
32pub struct GenericWebFilter<Q, S, A, J>
33where
34 Q: WebLeaf + Serialize,
35 S: WebSortField + Serialize,
36 A: WebLeaf + Serialize,
37 J: WebJoinPayload + Serialize,
38{
39 #[schema(nullable)]
40 pub joins: Option<Vec<JoinPayload<J>>>,
41 #[schema(no_recursion, nullable)]
42 pub filter: Option<GenericWebExpression<Q>>,
43 #[schema(no_recursion, nullable)]
44 pub having: Option<GenericWebExpression<A>>,
45 #[schema(no_recursion, nullable)]
46 pub sort: Option<Vec<GenericWebSort<S>>>,
47 #[schema(no_recursion, nullable)]
48 pub search: Option<WebSearch>,
49 #[schema(nullable)]
50 pub page: Option<WebPagination>,
51}
52
53#[derive(Clone, Serialize, Deserialize, ToSchema, Debug)]
54#[serde(bound(deserialize = "Q: WebLeaf + Deserialize<'de>"))]
55#[serde(untagged)]
56pub enum GenericWebExpression<Q>
57where
58 Q: WebLeaf + Serialize,
59{
60 #[schema(no_recursion)]
61 And {
62 and: Vec<GenericWebExpression<Q>>,
63 },
64 #[schema(no_recursion)]
65 Or {
66 or: Vec<GenericWebExpression<Q>>,
67 },
68 Leaf(Q),
69}
70
71#[derive(Clone, Serialize, Deserialize, ToSchema, Debug)]
72#[serde(bound(deserialize = "S: WebSortField + Deserialize<'de>"))]
73#[serde(transparent)]
74pub struct GenericWebSort<S>(pub S)
75where
76 S: WebSortField + Serialize;
77
78#[derive(Clone, Serialize, Deserialize, ToSchema, Debug)]
79#[serde(rename_all = "camelCase")]
80pub struct WebSearch {
81 pub query: String,
82 #[serde(default, skip_serializing_if = "Option::is_none")]
83 pub language: Option<String>,
84 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub include_rank: Option<bool>,
86}
87
88pub type WebExpression<T> = GenericWebExpression<<T as WebQueryModel>::Leaf>;
89pub type WebAggregateExpression<T> =
90 GenericWebExpression<<T as WebQueryModel>::AggregateLeaf>;
91pub type WebSort<T> = GenericWebSort<<T as WebQueryModel>::SortField>;
92pub type WebFilter<T> = GenericWebFilter<
93 <T as WebQueryModel>::Leaf,
94 <T as WebQueryModel>::SortField,
95 <T as WebQueryModel>::AggregateLeaf,
96 <T as WebQueryModel>::JoinPath,
97>;
98
99pub trait AggregateBindable<C>: WebQueryModel
100where
101 C: QueryContext,
102{
103 fn map_aggregate_leaf(
104 leaf: &<Self as WebQueryModel>::AggregateLeaf,
105 ) -> HavingPredicate;
106}
107
108#[derive(Clone, Serialize, Deserialize, ToSchema, Debug)]
109pub enum NoJoins {}
110
111impl WebJoinPayload for NoJoins {
112 fn flatten(&self, _prefix: &mut Vec<String>, _out: &mut Vec<Vec<String>>) {
113 match *self {}
114 }
115}
116
117#[derive(Clone, Serialize, Deserialize, ToSchema, Debug)]
118#[serde(transparent)]
119pub struct JoinPayload<J>(pub J);
120
121impl<J> JoinPayload<J> {
122 pub fn inner(&self) -> &J {
123 &self.0
124 }
125}