supabase_client_query/
modifier.rs1use crate::sql::{CountOption, NullsPosition, OrderClause, OrderDirection, SqlParts, validate_column_name};
2
3pub trait Modifiable: Sized {
5 fn parts_mut(&mut self) -> &mut SqlParts;
7
8 fn order(mut self, column: &str, direction: OrderDirection) -> Self {
10 if let Err(e) = validate_column_name(column) {
11 tracing::error!("Invalid column name in order: {e}");
12 return self;
13 }
14 self.parts_mut().orders.push(OrderClause {
15 column: column.to_string(),
16 direction,
17 nulls: None,
18 });
19 self
20 }
21
22 fn order_with_nulls(
24 mut self,
25 column: &str,
26 direction: OrderDirection,
27 nulls: NullsPosition,
28 ) -> Self {
29 if let Err(e) = validate_column_name(column) {
30 tracing::error!("Invalid column name in order_with_nulls: {e}");
31 return self;
32 }
33 self.parts_mut().orders.push(OrderClause {
34 column: column.to_string(),
35 direction,
36 nulls: Some(nulls),
37 });
38 self
39 }
40
41 fn limit(mut self, count: i64) -> Self {
43 self.parts_mut().limit = Some(count);
44 self
45 }
46
47 fn range(mut self, from: i64, to: i64) -> Self {
49 self.parts_mut().offset = Some(from);
50 self.parts_mut().limit = Some(to - from + 1);
51 self
52 }
53
54 fn single(mut self) -> Self {
56 self.parts_mut().single = true;
57 self.parts_mut().limit = Some(2); self
59 }
60
61 fn maybe_single(mut self) -> Self {
63 self.parts_mut().maybe_single = true;
64 self.parts_mut().limit = Some(2);
65 self
66 }
67
68 fn count(mut self) -> Self {
70 self.parts_mut().count = CountOption::Exact;
71 self
72 }
73
74 fn count_option(mut self, option: CountOption) -> Self {
76 self.parts_mut().count = option;
77 self
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use crate::backend::QueryBackend;
85 use crate::select::SelectBuilder;
86 use crate::sql::*;
87 use std::marker::PhantomData;
88 use std::sync::Arc;
89
90 fn make_select() -> SelectBuilder<supabase_client_core::Row> {
91 SelectBuilder {
92 backend: QueryBackend::Rest {
93 http: reqwest::Client::new(),
94 base_url: Arc::from("http://localhost"),
95 api_key: Arc::from("key"),
96 schema: "public".to_string(),
97 },
98 parts: SqlParts::new(SqlOperation::Select, "public", "test"),
99 params: ParamStore::new(),
100 _marker: PhantomData,
101 }
102 }
103
104 #[test]
105 fn test_order_ascending() {
106 let builder = make_select().order("name", OrderDirection::Ascending);
107 assert_eq!(builder.parts.orders.len(), 1);
108 assert_eq!(builder.parts.orders[0].column, "name");
109 assert_eq!(builder.parts.orders[0].direction, OrderDirection::Ascending);
110 assert!(builder.parts.orders[0].nulls.is_none());
111 }
112
113 #[test]
114 fn test_order_descending() {
115 let builder = make_select().order("created_at", OrderDirection::Descending);
116 assert_eq!(builder.parts.orders.len(), 1);
117 assert_eq!(builder.parts.orders[0].column, "created_at");
118 assert_eq!(builder.parts.orders[0].direction, OrderDirection::Descending);
119 }
120
121 #[test]
122 fn test_order_with_nulls_first() {
123 let builder = make_select().order_with_nulls(
124 "score",
125 OrderDirection::Descending,
126 NullsPosition::First,
127 );
128 assert_eq!(builder.parts.orders.len(), 1);
129 assert_eq!(builder.parts.orders[0].column, "score");
130 assert_eq!(builder.parts.orders[0].direction, OrderDirection::Descending);
131 assert_eq!(builder.parts.orders[0].nulls, Some(NullsPosition::First));
132 }
133
134 #[test]
135 fn test_order_with_nulls_last() {
136 let builder = make_select().order_with_nulls(
137 "score",
138 OrderDirection::Ascending,
139 NullsPosition::Last,
140 );
141 assert_eq!(builder.parts.orders[0].nulls, Some(NullsPosition::Last));
142 }
143
144 #[test]
145 fn test_order_invalid_column_ignored() {
146 let builder = make_select().order("bad;col", OrderDirection::Ascending);
147 assert!(builder.parts.orders.is_empty());
148 }
149
150 #[test]
151 fn test_order_with_nulls_invalid_column_ignored() {
152 let builder = make_select().order_with_nulls(
153 "bad\"col",
154 OrderDirection::Ascending,
155 NullsPosition::First,
156 );
157 assert!(builder.parts.orders.is_empty());
158 }
159
160 #[test]
161 fn test_limit() {
162 let builder = make_select().limit(10);
163 assert_eq!(builder.parts.limit, Some(10));
164 }
165
166 #[test]
167 fn test_range() {
168 let builder = make_select().range(5, 14);
169 assert_eq!(builder.parts.offset, Some(5));
170 assert_eq!(builder.parts.limit, Some(10)); }
172
173 #[test]
174 fn test_range_single_row() {
175 let builder = make_select().range(0, 0);
176 assert_eq!(builder.parts.offset, Some(0));
177 assert_eq!(builder.parts.limit, Some(1)); }
179
180 #[test]
181 fn test_single() {
182 let builder = make_select().single();
183 assert!(builder.parts.single);
184 assert_eq!(builder.parts.limit, Some(2));
185 }
186
187 #[test]
188 fn test_maybe_single() {
189 let builder = make_select().maybe_single();
190 assert!(builder.parts.maybe_single);
191 assert_eq!(builder.parts.limit, Some(2));
192 }
193
194 #[test]
195 fn test_count() {
196 let builder = make_select().count();
197 assert_eq!(builder.parts.count, CountOption::Exact);
198 }
199
200 #[test]
201 fn test_count_option_exact() {
202 let builder = make_select().count_option(CountOption::Exact);
203 assert_eq!(builder.parts.count, CountOption::Exact);
204 }
205
206 #[test]
207 fn test_count_option_planned() {
208 let builder = make_select().count_option(CountOption::Planned);
209 assert_eq!(builder.parts.count, CountOption::Planned);
210 }
211
212 #[test]
213 fn test_count_option_estimated() {
214 let builder = make_select().count_option(CountOption::Estimated);
215 assert_eq!(builder.parts.count, CountOption::Estimated);
216 }
217
218 #[test]
219 fn test_count_option_none() {
220 let builder = make_select().count_option(CountOption::None);
221 assert_eq!(builder.parts.count, CountOption::None);
222 }
223}