1use sqlx::{
2 Postgres,
3 Type,
4};
5use sqlxo_traits::{
6 Filterable,
7 Sortable,
8 SqlJoin,
9};
10use sqlxo_traits::{
11 QueryContext,
12 SqlWrite,
13};
14
15mod expression;
16mod head;
17mod pagination;
18mod sort;
19
20pub use expression::Expression;
21pub use head::{
22 AggregationType,
23 DeleteHead,
24 InsertHead,
25 ReadHead,
26 SelectType,
27 UpdateHead,
28};
29pub use pagination::{
30 Page,
31 Pagination,
32};
33pub use sort::SortOrder;
34
35use crate::blocks::head::ToHead;
36
37pub trait BuildableFilter<C: QueryContext> {
42 fn r#where(self, e: Expression<C::Query>) -> Self;
43}
44
45pub trait BuildableJoin<C: QueryContext> {
46 fn join(self, j: C::Join) -> Self;
47}
48
49pub trait BuildableSort<C: QueryContext> {
50 fn order_by(self, s: SortOrder<C::Sort>) -> Self;
51}
52
53pub trait BuildablePage<C: QueryContext> {
54 fn paginate(self, p: Pagination) -> Self;
55}
56
57pub struct SqlWriter {
58 qb: sqlx::QueryBuilder<'static, Postgres>,
59 has_join: bool,
60 has_where: bool,
61 has_sort: bool,
62 has_pagination: bool,
63}
64
65impl SqlWriter {
66 pub fn new(head: impl ToHead) -> Self {
67 let qb =
68 sqlx::QueryBuilder::<Postgres>::new(head.to_head().to_string());
69
70 Self {
71 qb,
72 has_join: false,
73 has_where: false,
74 has_sort: false,
75 has_pagination: false,
76 }
77 }
78
79 pub fn into_builder(self) -> sqlx::QueryBuilder<'static, Postgres> {
80 self.qb
81 }
82
83 pub fn query_builder_mut(
86 &mut self,
87 ) -> &mut sqlx::QueryBuilder<'static, Postgres> {
88 &mut self.qb
89 }
90
91 pub fn push_joins<J: SqlJoin>(&mut self, joins: &Vec<J>) {
92 if self.has_join {
93 return;
94 }
95
96 for j in joins {
97 self.qb.push(j.to_sql());
98 }
99 }
100
101 pub fn push_where<F: Filterable>(&mut self, expr: &Expression<F>) {
102 if self.has_where {
103 return;
104 }
105
106 self.qb.push(" WHERE ");
107 self.has_where = true;
108 expr.write(self);
109 }
110
111 pub fn push_soft_delete_filter<F: Filterable>(
112 &mut self,
113 delete_field: &str,
114 existing_expr: Option<&Expression<F>>,
115 ) {
116 if self.has_where {
117 return;
118 }
119
120 self.qb.push(" WHERE ");
121 self.has_where = true;
122
123 self.qb.push(delete_field);
125 self.qb.push(" IS NULL");
126
127 if let Some(expr) = existing_expr {
129 self.qb.push(" AND (");
130 expr.write(self);
131 self.qb.push(")");
132 }
133 }
134
135 pub fn push_sort<S: Sortable>(&mut self, sort: &SortOrder<S>) {
136 if self.has_sort {
137 return;
138 }
139
140 self.qb.push(" ORDER BY ");
141 self.has_sort = true;
142 self.qb.push(sort.to_sql());
143 }
144
145 pub fn push_pagination(&mut self, p: &Pagination) {
146 if self.has_pagination {
147 return;
148 }
149
150 self.qb.push(" LIMIT ");
151 self.bind(p.page_size);
152 self.qb.push(" OFFSET ");
153 self.bind(p.page * p.page_size);
154 }
155}
156
157impl SqlWrite for SqlWriter {
158 fn push(&mut self, s: &str) {
159 self.qb.push(s);
160 }
161
162 fn bind<T>(&mut self, value: T)
163 where
164 T: sqlx::Encode<'static, Postgres> + Send + 'static,
165 T: Type<Postgres>,
166 {
167 self.qb.push_bind(value);
168 }
169}