post_archiver/query/
filter.rs1use std::{collections::HashSet, fmt::Display, hash::Hash, ops::Deref, rc::Rc};
16
17use chrono::{DateTime, Utc};
18use rusqlite::ToSql;
19use serde::Serialize;
20
21use crate::query::RawSql;
22
23#[derive(Debug)]
32pub struct TextFilter {
33 col: &'static str,
34 text: String,
35}
36
37impl Deref for TextFilter {
38 type Target = String;
39
40 fn deref(&self) -> &Self::Target {
41 &self.text
42 }
43}
44
45impl TextFilter {
46 pub fn new(col: &'static str) -> Self {
48 TextFilter {
49 col,
50 text: String::new(),
51 }
52 }
53
54 pub fn contains(&mut self, text: &str) -> &mut Self {
56 self.text = format!("%{}%", Self::safe_escape(text));
57 self
58 }
59
60 pub fn starts_with(&mut self, text: &str) -> &mut Self {
62 self.text = format!("{}%", Self::safe_escape(text));
63 self
64 }
65
66 pub fn ends_with(&mut self, text: &str) -> &mut Self {
68 self.text = format!("%{}", Self::safe_escape(text));
69 self
70 }
71
72 pub fn equals(&mut self, text: &str) -> &mut Self {
74 self.text = Self::safe_escape(text);
75 self
76 }
77
78 pub fn like(&mut self, t: &str) -> &mut Self {
81 self.text = t.to_string();
82 self
83 }
84
85 fn safe_escape(text: &str) -> String {
86 text.replace('%', "\\%").replace('_', "\\_")
87 }
88
89 pub fn build_sql<T>(&self, mut sql: RawSql<T>) -> RawSql<T> {
90 if self.text.is_empty() {
91 return sql;
92 }
93
94 let (wheres, params) = &mut sql.where_clause;
95 wheres.push(format!("{} LIKE ?", self.col));
96 params.push(Rc::new(self.text.clone()));
97 sql
98 }
99}
100
101#[derive(Debug)]
106pub struct DateFilter {
107 col: &'static str,
108 before: Option<DateTime<Utc>>,
109 after: Option<DateTime<Utc>>,
110}
111
112impl DateFilter {
113 pub fn new(col: &'static str) -> Self {
115 DateFilter {
116 col,
117 before: None,
118 after: None,
119 }
120 }
121
122 pub fn before(&mut self, date: DateTime<Utc>) -> &mut Self {
124 self.before = Some(date);
125 self
126 }
127
128 pub fn after(&mut self, date: DateTime<Utc>) -> &mut Self {
130 self.after = Some(date);
131 self
132 }
133
134 pub fn equals(&mut self, date: DateTime<Utc>) -> &mut Self {
136 self.before = Some(date);
137 self.after = Some(date);
138 self
139 }
140
141 pub fn build_sql<T>(&self, mut sql: RawSql<T>) -> RawSql<T> {
142 let (wheres, params) = &mut sql.where_clause;
143 match (self.before, self.after) {
144 (None, None) => {}
145 (Some(before), None) => {
146 wheres.push(format!("{} <= ?", self.col));
147 params.push(Rc::new(before));
148 }
149 (None, Some(after)) => {
150 wheres.push(format!("{} >= ?", self.col));
151 params.push(Rc::new(after));
152 }
153 (Some(before), Some(after)) => {
154 if before == after {
155 wheres.push(format!("{} = ?", self.col));
156 params.push(Rc::new(before));
157 } else {
158 wheres.push(format!("{} BETWEEN ? AND ?", self.col));
159 params.push(Rc::new(after));
160 params.push(Rc::new(before));
161 }
162 }
163 }
164 sql
165 }
166}
167
168#[derive(Debug)]
176pub struct IdFilter<T> {
177 col: &'static str,
178 ids: HashSet<T>,
179}
180
181impl<T> Deref for IdFilter<T> {
182 type Target = HashSet<T>;
183
184 fn deref(&self) -> &Self::Target {
185 &self.ids
186 }
187}
188
189impl<T> IdFilter<T>
190where
191 T: Hash + PartialEq + Eq + ToSql + Serialize + Display + Clone + 'static,
192{
193 pub fn new(col: &'static str) -> Self {
195 IdFilter {
196 col,
197 ids: HashSet::new(),
198 }
199 }
200
201 pub fn insert(&mut self, id: T) -> &mut Self {
203 self.ids.insert(id);
204 self
205 }
206
207 pub fn extend(&mut self, ids: impl IntoIterator<Item = T>) -> &mut Self {
209 self.ids.extend(ids);
210 self
211 }
212
213 pub fn build_sql<U>(&self, mut sql: RawSql<U>) -> RawSql<U> {
215 let (wheres, params) = &mut sql.where_clause;
216 match self.ids.len() {
217 0 => {}
218 1 => {
219 wheres.push(format!("{} = ?", self.col));
220 params.push(Rc::new(self.ids.iter().next().unwrap().clone()));
221 }
222 _ => {
223 wheres.push(format!("{} IN (SELECT value FROM json_each(?))", self.col));
224 let json_array = serde_json::to_string(&self.ids).unwrap();
225 params.push(Rc::new(json_array));
226 }
227 }
228 sql
229 }
230}
231
232#[derive(Debug)]
243pub struct RelationshipsFilter<T> {
244 table: &'static str,
245 col: &'static str,
246 ids: HashSet<T>,
247}
248
249impl<T> Deref for RelationshipsFilter<T> {
250 type Target = HashSet<T>;
251
252 fn deref(&self) -> &Self::Target {
253 &self.ids
254 }
255}
256
257impl<T> RelationshipsFilter<T> {
258 pub fn new(table: &'static str, col: &'static str) -> Self {
260 RelationshipsFilter {
261 table,
262 col,
263 ids: HashSet::new(),
264 }
265 }
266}
267
268impl<T: Eq + std::hash::Hash> RelationshipsFilter<T> {
269 pub fn insert(&mut self, id: T) -> &mut Self {
271 self.ids.insert(id);
272 self
273 }
274
275 pub fn extend(&mut self, ids: impl IntoIterator<Item = T>) -> &mut Self {
277 self.ids.extend(ids);
278 self
279 }
280}
281
282impl<T: Display + ToSql + Serialize + Clone + 'static> RelationshipsFilter<T>
283where
284 T: Eq + std::hash::Hash,
285{
286 pub fn build_sql<U>(&self, mut sql: RawSql<U>) -> RawSql<U> {
287 let (wheres, params) = &mut sql.where_clause;
288 match self.len() {
289 0 => {}
290 1 => {
291 wheres.push(format!(
292 "EXISTS (SELECT 1 FROM {} WHERE post = posts.id AND {} = ?)",
293 self.table, self.col
294 ));
295 params.push(Rc::new(self.iter().next().unwrap().clone()));
296 }
297 n => {
298 wheres.push(format!(
299 "? == (SELECT COUNT(*) FROM {} WHERE post = posts.id AND {} IN (SELECT value FROM json_each(?)))",
300 self.table, self.col
301 ));
302 params.push(Rc::new(n));
303 let json_array = serde_json::to_string(&self.ids).unwrap();
304 params.push(Rc::new(json_array));
305 }
306 }
307 sql
308 }
309}