chuchi_postgres/filter/
mod.rs

1use std::borrow::Cow;
2use std::fmt;
3
4use tokio_postgres::types::ToSql;
5use types::time::{Date, DateTime, Timeout};
6use types::uid::UniqueId;
7
8mod whr;
9
10pub type SqlStr = Cow<'static, str>;
11
12#[derive(Debug)]
13#[non_exhaustive]
14pub struct Filter<'a> {
15	pub whr: Where,
16	pub order_by: OrderBy,
17	pub limit: Limit,
18	pub offset: Offset,
19	pub params: Params<'a>,
20}
21
22impl<'a> Filter<'a> {
23	pub fn new() -> Self {
24		Self {
25			whr: Where::new(),
26			order_by: OrderBy::new(),
27			limit: Limit::new(),
28			offset: Offset::new(),
29			params: Params::new(),
30		}
31	}
32
33	pub fn and_where(&mut self, whr: WhereFilter<'a>) {
34		self.whr.push(WherePart::And);
35		self.whr.append(whr.whr);
36		self.params.append(whr.params);
37	}
38
39	pub fn or_where(&mut self, whr: WhereFilter<'a>) {
40		self.whr.push(WherePart::Or);
41		self.whr.append(whr.whr);
42		self.params.append(whr.params);
43	}
44
45	pub(crate) fn to_formatter(&'a self) -> FilterFormatter<'a> {
46		FilterFormatter {
47			whr: &self.whr,
48			order_by: &self.order_by,
49			limit: &self.limit,
50			offset: &self.offset,
51			params: &self.params,
52		}
53	}
54}
55
56impl fmt::Display for Filter<'_> {
57	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58		write!(f, "{}", self.to_formatter())
59	}
60}
61
62#[derive(Debug)]
63#[non_exhaustive]
64pub(crate) struct FilterFormatter<'a> {
65	pub whr: &'a Where,
66	pub order_by: &'a OrderBy,
67	pub limit: &'a Limit,
68	pub offset: &'a Offset,
69	pub params: &'a Params<'a>,
70}
71
72impl fmt::Display for FilterFormatter<'_> {
73	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74		self.whr.fmt(f)?;
75
76		self.order_by.fmt(f)?;
77
78		let offset_has_param = matches!(self.offset, Offset::Param);
79		let param_count =
80			self.params.len() - if offset_has_param { 1 } else { 0 };
81
82		match &self.limit {
83			Limit::Fixed(value) => write!(f, " LIMIT {}", value)?,
84			Limit::Param => {
85				write!(f, " LIMIT ${}", param_count)?;
86			}
87			Limit::All => {}
88		}
89
90		match &self.offset {
91			Offset::Zero => {}
92			Offset::Fixed(value) => write!(f, " OFFSET {}", value)?,
93			Offset::Param => {
94				write!(f, " OFFSET ${}", self.params.len())?;
95			}
96		}
97
98		Ok(())
99	}
100}
101
102#[derive(Debug)]
103#[non_exhaustive]
104pub struct WhereFilter<'a> {
105	pub whr: Where,
106	pub params: Params<'a>,
107}
108
109impl<'a> WhereFilter<'a> {
110	pub fn new() -> Self {
111		Self {
112			whr: Where::new(),
113			params: Params::new(),
114		}
115	}
116}
117
118impl fmt::Display for WhereFilter<'_> {
119	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120		self.whr.fmt(f)
121	}
122}
123
124#[derive(Debug, Default)]
125pub struct Where {
126	inner: Vec<WherePart>,
127}
128
129#[derive(Debug)]
130#[non_exhaustive]
131pub enum WherePart {
132	Operation(WhereOperation),
133	And,
134	Or,
135	Nested(Where),
136}
137
138#[derive(Debug)]
139pub struct WhereOperation {
140	pub kind: Operator,
141	pub column: Cow<'static, str>,
142}
143
144#[derive(Debug)]
145#[non_exhaustive]
146pub enum Operator {
147	Eq,
148	Ne,
149	Lt,
150	Lte,
151	Gt,
152	Gte,
153	Like,
154	In { length: usize },
155
156	// rhs will be ignored
157	IsNull,
158	// rhs will be ignored
159	IsNotNull,
160}
161
162#[derive(Debug)]
163#[non_exhaustive]
164pub enum WhereIdent {
165	Param,
166	Name(Cow<'static, str>),
167}
168
169impl Where {
170	pub fn new() -> Self {
171		Self { inner: vec![] }
172	}
173
174	pub fn push(&mut self, part: impl Into<WherePart>) {
175		self.inner.push(part.into());
176	}
177
178	fn append(&mut self, other: Where) {
179		self.inner.extend(other.inner);
180	}
181
182	fn is_empty(&self) -> bool {
183		self.inner.is_empty()
184	}
185
186	pub(crate) fn to_formatter<'a>(&'a self) -> WhereFormatter<'a> {
187		WhereFormatter {
188			whr: &self,
189			param_start: 0,
190		}
191	}
192}
193
194impl<'a> fmt::Display for Where {
195	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196		self.to_formatter().fmt(f)
197	}
198}
199
200pub(crate) struct WhereFormatter<'a> {
201	pub whr: &'a Where,
202	/// indexed by zero
203	pub param_start: usize,
204}
205
206impl<'a> WhereFormatter<'a> {
207	/// you need to print " WHERE " before calling this
208	fn fmt_inner(
209		&self,
210		f: &mut fmt::Formatter<'_>,
211	) -> Result<usize, fmt::Error> {
212		let mut param_num = self.param_start;
213
214		for part in &self.whr.inner {
215			match part {
216				WherePart::And => f.write_str(" AND ")?,
217				WherePart::Or => f.write_str(" OR ")?,
218				WherePart::Nested(inner) => {
219					let mut inner = inner.to_formatter();
220					inner.param_start = param_num;
221					f.write_str("(")?;
222					param_num = inner.fmt_inner(f)?;
223					f.write_str(")")?;
224				}
225				WherePart::Operation(op) => match &op.kind {
226					Operator::IsNull | Operator::IsNotNull => {
227						write!(f, "\"{}\" {}", op.column, op.kind.as_str())?;
228					}
229					// handle in special if the length is zero
230					// in this case we wan't the query to always return no results
231					Operator::In { length } if *length == 0 => {
232						write!(f, "1=0")?;
233					}
234					Operator::In { length } => {
235						write!(f, "\"{}\" IN (", op.column)?;
236
237						for i in 0..*length {
238							if i != 0 {
239								f.write_str(", ")?;
240							}
241
242							param_num += 1;
243							write!(f, "${}", param_num)?;
244						}
245
246						f.write_str(")")?;
247					}
248					o => {
249						param_num += 1;
250
251						write!(
252							f,
253							"\"{}\" {} ${}",
254							op.column,
255							o.as_str(),
256							param_num
257						)?;
258					}
259				},
260			}
261		}
262
263		Ok(param_num)
264	}
265}
266
267impl<'a> fmt::Display for WhereFormatter<'a> {
268	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269		if self.whr.is_empty() {
270			return Ok(());
271		}
272
273		f.write_str(" WHERE ")?;
274
275		self.fmt_inner(f)?;
276
277		Ok(())
278	}
279}
280
281impl From<WhereOperation> for WherePart {
282	fn from(op: WhereOperation) -> Self {
283		Self::Operation(op)
284	}
285}
286
287impl Operator {
288	fn as_str(&self) -> &str {
289		match self {
290			Operator::Eq => "=",
291			Operator::Ne => "!=",
292			Operator::Lt => "<",
293			Operator::Lte => "<=",
294			Operator::Gt => ">",
295			Operator::Gte => ">=",
296			Operator::Like => "LIKE",
297			Operator::In { .. } => "IN",
298			Operator::IsNull => "IS NULL",
299			Operator::IsNotNull => "IS NOT NULL",
300		}
301	}
302}
303
304#[derive(Debug)]
305pub struct OrderBy {
306	inner: Vec<OrderByPart>,
307}
308
309#[derive(Debug)]
310pub enum OrderByPart {
311	Asc(Cow<'static, str>),
312	Desc(Cow<'static, str>),
313}
314
315impl OrderBy {
316	pub fn new() -> Self {
317		Self { inner: vec![] }
318	}
319
320	pub fn push_asc(&mut self, column: impl Into<Cow<'static, str>>) {
321		self.inner.push(OrderByPart::Asc(column.into()));
322	}
323
324	pub fn push_desc(&mut self, column: impl Into<Cow<'static, str>>) {
325		self.inner.push(OrderByPart::Desc(column.into()));
326	}
327
328	fn is_empty(&self) -> bool {
329		self.inner.is_empty()
330	}
331}
332
333impl fmt::Display for OrderBy {
334	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335		if self.is_empty() {
336			return Ok(());
337		}
338
339		f.write_str(" ORDER BY ")?;
340
341		for (i, part) in self.inner.iter().enumerate() {
342			if i != 0 {
343				f.write_str(", ")?;
344			}
345
346			match part {
347				OrderByPart::Asc(column) => write!(f, "\"{}\" ASC", column)?,
348				OrderByPart::Desc(column) => write!(f, "\"{}\" DESC", column)?,
349			}
350		}
351
352		Ok(())
353	}
354}
355
356#[derive(Debug)]
357pub enum Limit {
358	Fixed(usize),
359	Param,
360	All,
361}
362
363impl Limit {
364	pub fn new() -> Self {
365		Self::All
366	}
367
368	pub fn set_param(&mut self) {
369		*self = Self::Param;
370	}
371
372	pub fn set_fixed(&mut self, value: usize) {
373		*self = Self::Fixed(value);
374	}
375}
376
377#[derive(Debug)]
378pub enum Offset {
379	Zero,
380	Fixed(usize),
381	Param,
382}
383
384impl Offset {
385	pub fn new() -> Self {
386		Self::Zero
387	}
388
389	pub fn set_param(&mut self) {
390		*self = Self::Param;
391	}
392
393	pub fn set_fixed(&mut self, value: usize) {
394		*self = Self::Fixed(value);
395	}
396}
397
398#[derive(Debug)]
399pub struct Params<'a> {
400	inner: Vec<Param<'a>>,
401}
402
403impl<'a> Params<'a> {
404	pub fn new() -> Self {
405		Self { inner: vec![] }
406	}
407
408	pub fn push(&mut self, param: Param<'a>) {
409		self.inner.push(param);
410	}
411
412	pub fn len(&self) -> usize {
413		self.inner.len()
414	}
415
416	pub fn is_empty(&self) -> bool {
417		self.inner.is_empty()
418	}
419
420	pub fn iter_to_sql(
421		&self,
422	) -> impl ExactSizeIterator<Item = &(dyn ToSql + Sync)> {
423		self.inner.iter().map(|p| p.data.as_ref())
424	}
425
426	fn append(&mut self, other: Params<'a>) {
427		self.inner.extend(other.inner);
428	}
429}
430
431#[derive(Debug)]
432#[non_exhaustive]
433pub struct Param<'a> {
434	// todo is the name still needed?
435	pub name: &'static str,
436	pub data: CowParamData<'a>,
437	is_null: bool,
438}
439
440impl<'a> Param<'a> {
441	pub fn new<T>(name: &'static str, data: &'a T) -> Self
442	where
443		T: ParamData + ToSql + Send + Sync,
444	{
445		Self {
446			name,
447			is_null: data.is_null(),
448			data: CowParamData::Borrowed(data),
449		}
450	}
451
452	pub fn new_owned<T>(name: &'static str, data: T) -> Self
453	where
454		T: ParamData + ToSql + Send + Sync + 'static,
455	{
456		Self {
457			name,
458			is_null: data.is_null(),
459			data: CowParamData::Owned(Box::new(data)),
460		}
461	}
462
463	pub fn is_null(&self) -> bool {
464		self.is_null
465	}
466}
467
468#[derive(Debug)]
469pub enum CowParamData<'a> {
470	Borrowed(&'a (dyn ToSql + Send + Sync)),
471	Owned(Box<dyn ToSql + Send + Sync>),
472}
473
474impl<'a> CowParamData<'a> {
475	pub fn as_ref(&self) -> &(dyn ToSql + Sync) {
476		match self {
477			CowParamData::Borrowed(data) => *data,
478			CowParamData::Owned(data) => &**data,
479		}
480	}
481}
482
483pub trait ParamData {
484	fn is_null(&self) -> bool;
485}
486
487impl<T> ParamData for &T
488where
489	T: ParamData + ?Sized,
490{
491	fn is_null(&self) -> bool {
492		(**self).is_null()
493	}
494}
495
496impl<T> ParamData for &mut T
497where
498	T: ParamData + ?Sized,
499{
500	fn is_null(&self) -> bool {
501		(**self).is_null()
502	}
503}
504
505impl<T> ParamData for Option<T> {
506	fn is_null(&self) -> bool {
507		self.is_none()
508	}
509}
510
511impl<T> ParamData for Vec<T> {
512	fn is_null(&self) -> bool {
513		// todo should an empty array be considered null?
514		false
515	}
516}
517
518impl<T> ParamData for [T] {
519	fn is_null(&self) -> bool {
520		// todo should an empty array be considered null?
521		false
522	}
523}
524
525impl<'a, T> ParamData for Cow<'a, T>
526where
527	T: ToOwned + ParamData,
528{
529	fn is_null(&self) -> bool {
530		(**self).is_null()
531	}
532}
533
534#[macro_export]
535macro_rules! param_not_null {
536	($impl_for:ty) => {
537		impl $crate::filter::ParamData for $impl_for {
538			fn is_null(&self) -> bool {
539				false
540			}
541		}
542	};
543
544	($( $impl_for:ty ),*) => {
545		$(
546			$crate::param_not_null!($impl_for);
547		)*
548	};
549}
550
551param_not_null!(
552	String, str, bool, f64, f32, i64, i32, i16, i8, UniqueId, DateTime, Date,
553	Timeout
554);
555
556#[cfg(feature = "email")]
557impl ParamData for email_address::EmailAddress {
558	fn is_null(&self) -> bool {
559		false
560	}
561}