1use crate::validate::assert_valid_sql_identifier;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7#[non_exhaustive]
8pub enum Operator {
9 Eq,
11 Ne,
13 Gt,
15 Gte,
17 Lt,
19 Lte,
21 In,
23 NotIn,
25 Regex,
27 Like,
29 ILike,
31 StartsWith,
33 EndsWith,
35 Contains,
37 Between,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43#[non_exhaustive]
44pub enum LogicalOp {
45 And,
47 Or,
49 Not,
51}
52
53#[derive(Debug, Clone, PartialEq)]
55#[non_exhaustive]
56pub enum FilterExpr {
57 Simple(Filter),
59 Compound(CompoundFilter),
61}
62
63#[derive(Debug, Clone, PartialEq)]
65#[non_exhaustive]
66pub struct CompoundFilter {
67 pub op: LogicalOp,
69 pub filters: Vec<FilterExpr>,
71}
72
73impl CompoundFilter {
74 #[must_use]
76 pub const fn and(filters: Vec<FilterExpr>) -> Self {
77 Self {
78 op: LogicalOp::And,
79 filters,
80 }
81 }
82
83 #[must_use]
85 pub const fn or(filters: Vec<FilterExpr>) -> Self {
86 Self {
87 op: LogicalOp::Or,
88 filters,
89 }
90 }
91
92 #[must_use]
94 pub fn not(filter: FilterExpr) -> Self {
95 Self {
96 op: LogicalOp::Not,
97 filters: vec![filter],
98 }
99 }
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104#[non_exhaustive]
105pub enum AggregateFunc {
106 Count,
108 CountDistinct,
110 Sum,
112 Avg,
114 Min,
116 Max,
118}
119
120#[derive(Debug, Clone, PartialEq, Eq)]
122#[non_exhaustive]
123pub struct Aggregate {
124 pub func: AggregateFunc,
126 pub field: Option<String>,
128 pub alias: Option<String>,
130}
131
132impl Aggregate {
133 #[must_use]
135 pub fn count() -> Self {
136 Self {
137 func: AggregateFunc::Count,
138 field: None,
139 alias: Some("count".to_string()),
140 }
141 }
142
143 pub fn count_field(field: impl Into<String>) -> Self {
149 let field = field.into();
150 assert_valid_sql_identifier(&field, "aggregate field");
151 Self {
152 func: AggregateFunc::Count,
153 field: Some(field),
154 alias: None,
155 }
156 }
157
158 pub fn count_distinct(field: impl Into<String>) -> Self {
164 let field = field.into();
165 assert_valid_sql_identifier(&field, "aggregate field");
166 Self {
167 func: AggregateFunc::CountDistinct,
168 field: Some(field),
169 alias: None,
170 }
171 }
172
173 pub fn sum(field: impl Into<String>) -> Self {
179 let field = field.into();
180 assert_valid_sql_identifier(&field, "aggregate field");
181 Self {
182 func: AggregateFunc::Sum,
183 field: Some(field),
184 alias: None,
185 }
186 }
187
188 pub fn avg(field: impl Into<String>) -> Self {
194 let field = field.into();
195 assert_valid_sql_identifier(&field, "aggregate field");
196 Self {
197 func: AggregateFunc::Avg,
198 field: Some(field),
199 alias: None,
200 }
201 }
202
203 pub fn min(field: impl Into<String>) -> Self {
209 let field = field.into();
210 assert_valid_sql_identifier(&field, "aggregate field");
211 Self {
212 func: AggregateFunc::Min,
213 field: Some(field),
214 alias: None,
215 }
216 }
217
218 pub fn max(field: impl Into<String>) -> Self {
224 let field = field.into();
225 assert_valid_sql_identifier(&field, "aggregate field");
226 Self {
227 func: AggregateFunc::Max,
228 field: Some(field),
229 alias: None,
230 }
231 }
232
233 pub fn as_alias(mut self, alias: impl Into<String>) -> Self {
239 let alias = alias.into();
240 assert_valid_sql_identifier(&alias, "aggregate alias");
241 self.alias = Some(alias);
242 self
243 }
244
245 #[must_use]
247 pub fn to_sql(&self) -> String {
248 let expr = match (&self.func, &self.field) {
249 (AggregateFunc::Count, None) => "COUNT(*)".to_string(),
250 (AggregateFunc::Count, Some(f)) => format!("COUNT({f})"),
251 (AggregateFunc::CountDistinct, Some(f)) => format!("COUNT(DISTINCT {f})"),
252 (AggregateFunc::Sum, Some(f)) => format!("SUM({f})"),
253 (AggregateFunc::Avg, Some(f)) => format!("AVG({f})"),
254 (AggregateFunc::Min, Some(f)) => format!("MIN({f})"),
255 (AggregateFunc::Max, Some(f)) => format!("MAX({f})"),
256 _ => "COUNT(*)".to_string(),
257 };
258
259 match &self.alias {
260 Some(a) => format!("{expr} AS {a}"),
261 None => expr,
262 }
263 }
264}
265
266#[derive(Debug, Clone, PartialEq)]
268#[non_exhaustive]
269pub enum Value {
270 Null,
272 Bool(bool),
274 Int(i64),
276 Float(f64),
278 String(String),
280 Array(Vec<Self>),
282}
283
284#[derive(Debug, Clone, Copy, PartialEq, Eq)]
286#[non_exhaustive]
287pub enum SortDir {
288 Asc,
290 Desc,
292}
293
294#[derive(Debug, Clone, PartialEq, Eq)]
296#[non_exhaustive]
297pub struct SortField {
298 pub field: String,
300 pub dir: SortDir,
302}
303
304impl SortField {
305 pub fn new(field: impl Into<String>, dir: SortDir) -> Self {
307 Self {
308 field: field.into(),
309 dir,
310 }
311 }
312
313 pub fn parse_sort_string(sort: &str, allowed: &[&str]) -> Result<Vec<Self>, String> {
323 let mut result = Vec::new();
324
325 for part in sort.split(',') {
326 let part = part.trim();
327 if part.is_empty() {
328 continue;
329 }
330
331 let (field, dir) = part
332 .strip_prefix('-')
333 .map_or((part, SortDir::Asc), |stripped| (stripped, SortDir::Desc));
334
335 if !allowed.is_empty() && !allowed.contains(&field) {
337 return Err(format!(
338 "Sort field '{field}' not allowed. Allowed: {allowed:?}"
339 ));
340 }
341
342 result.push(Self::new(field, dir));
343 }
344
345 Ok(result)
346 }
347}
348
349#[derive(Debug, Clone, PartialEq)]
351#[non_exhaustive]
352pub struct Filter {
353 pub field: String,
355 pub op: Operator,
357 pub value: Value,
359}
360
361impl Filter {
362 #[must_use]
364 pub fn new(field: impl Into<String>, op: Operator, value: Value) -> Self {
365 Self {
366 field: field.into(),
367 op,
368 value,
369 }
370 }
371}
372
373#[derive(Debug, Clone, PartialEq)]
375#[non_exhaustive]
376#[must_use = "QueryResult must be used to execute the query"]
377pub struct QueryResult {
378 pub sql: String,
380 pub params: Vec<Value>,
382}
383
384impl QueryResult {
385 #[must_use]
387 pub fn new(sql: impl Into<String>, params: Vec<Value>) -> Self {
388 Self {
389 sql: sql.into(),
390 params,
391 }
392 }
393}
394
395#[derive(Debug, Clone, PartialEq, Eq)]
397#[non_exhaustive]
398pub struct ComputedField {
399 pub alias: String,
401 pub expression: String,
403}
404
405impl ComputedField {
406 pub fn new(alias: impl Into<String>, expression: impl Into<String>) -> Self {
408 Self {
409 alias: alias.into(),
410 expression: expression.into(),
411 }
412 }
413
414 #[must_use]
416 pub fn to_sql(&self) -> String {
417 format!("({}) AS {}", self.expression, self.alias)
418 }
419}
420
421#[derive(Debug, Clone, Copy, PartialEq, Eq)]
423#[non_exhaustive]
424pub enum CursorDirection {
425 After,
427 Before,
429}
430
431pub fn simple(field: impl Into<String>, op: Operator, value: Value) -> FilterExpr {
437 let field = field.into();
438 assert_valid_sql_identifier(&field, "filter field");
439 FilterExpr::Simple(Filter { field, op, value })
440}
441
442#[must_use]
444pub const fn and(filters: Vec<FilterExpr>) -> FilterExpr {
445 FilterExpr::Compound(CompoundFilter::and(filters))
446}
447
448#[must_use]
450pub const fn or(filters: Vec<FilterExpr>) -> FilterExpr {
451 FilterExpr::Compound(CompoundFilter::or(filters))
452}
453
454#[must_use]
456pub fn not(filter: FilterExpr) -> FilterExpr {
457 FilterExpr::Compound(CompoundFilter::not(filter))
458}