1use wae_types::Value;
6
7#[cfg(feature = "turso")]
8use crate::types::from_wae_value;
9#[cfg(feature = "turso")]
10use turso::Value as TursoValue;
11
12#[cfg(feature = "mysql")]
13use crate::types::from_wae_to_mysql;
14#[cfg(feature = "mysql")]
15use mysql_async::Value as MySqlValue;
16
17#[cfg(feature = "postgres")]
18use crate::connection::postgres::{PostgresParam, value_to_postgres_param};
19
20#[derive(Debug, Clone)]
22pub enum Condition {
23 Eq {
25 column: String,
27 value: Value,
29 },
30 Ne {
32 column: String,
34 value: Value,
36 },
37 Gt {
39 column: String,
41 value: Value,
43 },
44 Gte {
46 column: String,
48 value: Value,
50 },
51 Lt {
53 column: String,
55 value: Value,
57 },
58 Lte {
60 column: String,
62 value: Value,
64 },
65 Like {
67 column: String,
69 pattern: String,
71 },
72 In {
74 column: String,
76 values: Vec<Value>,
78 },
79 IsNull {
81 column: String,
83 },
84 IsNotNull {
86 column: String,
88 },
89 And(Vec<Condition>),
91 Or(Vec<Condition>),
93 Not(Box<Condition>),
95 Raw {
97 sql: String,
99 params: Vec<Value>,
101 },
102}
103
104impl Condition {
105 pub fn eq<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
107 Condition::Eq { column: column.into(), value: value.into() }
108 }
109
110 pub fn ne<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
112 Condition::Ne { column: column.into(), value: value.into() }
113 }
114
115 pub fn gt<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
117 Condition::Gt { column: column.into(), value: value.into() }
118 }
119
120 pub fn gte<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
122 Condition::Gte { column: column.into(), value: value.into() }
123 }
124
125 pub fn lt<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
127 Condition::Lt { column: column.into(), value: value.into() }
128 }
129
130 pub fn lte<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
132 Condition::Lte { column: column.into(), value: value.into() }
133 }
134
135 pub fn like<C: Into<String>, P: Into<String>>(column: C, pattern: P) -> Self {
137 Condition::Like { column: column.into(), pattern: pattern.into() }
138 }
139
140 pub fn in_<C: Into<String>, V: Into<Value>>(column: C, values: Vec<V>) -> Self {
142 Condition::In { column: column.into(), values: values.into_iter().map(|v| v.into()).collect() }
143 }
144
145 pub fn is_null<C: Into<String>>(column: C) -> Self {
147 Condition::IsNull { column: column.into() }
148 }
149
150 pub fn is_not_null<C: Into<String>>(column: C) -> Self {
152 Condition::IsNotNull { column: column.into() }
153 }
154
155 pub fn and(conditions: Vec<Condition>) -> Self {
157 Condition::And(conditions)
158 }
159
160 pub fn or(conditions: Vec<Condition>) -> Self {
162 Condition::Or(conditions)
163 }
164
165 pub fn negate(condition: Condition) -> Self {
167 Condition::Not(Box::new(condition))
168 }
169
170 pub fn raw<S: Into<String>>(sql: S, params: Vec<Value>) -> Self {
172 Condition::Raw { sql: sql.into(), params }
173 }
174
175 #[cfg(feature = "turso")]
176 pub(crate) fn build_turso(&self) -> (String, Vec<TursoValue>) {
178 match self {
179 Condition::Eq { column, value } => (format!("{} = ?", column), vec![from_wae_value(value.clone())]),
180 Condition::Ne { column, value } => (format!("{} != ?", column), vec![from_wae_value(value.clone())]),
181 Condition::Gt { column, value } => (format!("{} > ?", column), vec![from_wae_value(value.clone())]),
182 Condition::Gte { column, value } => (format!("{} >= ?", column), vec![from_wae_value(value.clone())]),
183 Condition::Lt { column, value } => (format!("{} < ?", column), vec![from_wae_value(value.clone())]),
184 Condition::Lte { column, value } => (format!("{} <= ?", column), vec![from_wae_value(value.clone())]),
185 Condition::Like { column, pattern } => (format!("{} LIKE ?", column), vec![TursoValue::Text(pattern.clone())]),
186 Condition::In { column, values } => {
187 let placeholders: Vec<&str> = values.iter().map(|_| "?").collect();
188 let turso_values: Vec<TursoValue> = values.iter().map(|v| from_wae_value(v.clone())).collect();
189 (format!("{} IN ({})", column, placeholders.join(", ")), turso_values)
190 }
191 Condition::IsNull { column } => (format!("{} IS NULL", column), vec![]),
192 Condition::IsNotNull { column } => (format!("{} IS NOT NULL", column), vec![]),
193 Condition::And(conditions) => {
194 let mut sql_parts = Vec::new();
195 let mut all_params = Vec::new();
196 for cond in conditions {
197 let (sql, params) = cond.build_turso();
198 sql_parts.push(format!("({})", sql));
199 all_params.extend(params);
200 }
201 (sql_parts.join(" AND "), all_params)
202 }
203 Condition::Or(conditions) => {
204 let mut sql_parts = Vec::new();
205 let mut all_params = Vec::new();
206 for cond in conditions {
207 let (sql, params) = cond.build_turso();
208 sql_parts.push(format!("({})", sql));
209 all_params.extend(params);
210 }
211 (sql_parts.join(" OR "), all_params)
212 }
213 Condition::Not(cond) => {
214 let (sql, params) = cond.build_turso();
215 (format!("NOT ({})", sql), params)
216 }
217 Condition::Raw { sql, params } => {
218 let turso_params: Vec<TursoValue> = params.iter().map(|v| from_wae_value(v.clone())).collect();
219 (sql.clone(), turso_params)
220 }
221 }
222 }
223
224 #[cfg(feature = "mysql")]
225 pub(crate) fn build_mysql(&self) -> (String, Vec<MySqlValue>) {
227 match self {
228 Condition::Eq { column, value } => (format!("{} = ?", column), vec![from_wae_to_mysql(value.clone())]),
229 Condition::Ne { column, value } => (format!("{} != ?", column), vec![from_wae_to_mysql(value.clone())]),
230 Condition::Gt { column, value } => (format!("{} > ?", column), vec![from_wae_to_mysql(value.clone())]),
231 Condition::Gte { column, value } => (format!("{} >= ?", column), vec![from_wae_to_mysql(value.clone())]),
232 Condition::Lt { column, value } => (format!("{} < ?", column), vec![from_wae_to_mysql(value.clone())]),
233 Condition::Lte { column, value } => (format!("{} <= ?", column), vec![from_wae_to_mysql(value.clone())]),
234 Condition::Like { column, pattern } => {
235 (format!("{} LIKE ?", column), vec![MySqlValue::Bytes(pattern.clone().into_bytes())])
236 }
237 Condition::In { column, values } => {
238 let placeholders: Vec<&str> = values.iter().map(|_| "?").collect();
239 let mysql_values: Vec<MySqlValue> = values.iter().map(|v| from_wae_to_mysql(v.clone())).collect();
240 (format!("{} IN ({})", column, placeholders.join(", ")), mysql_values)
241 }
242 Condition::IsNull { column } => (format!("{} IS NULL", column), vec![]),
243 Condition::IsNotNull { column } => (format!("{} IS NOT NULL", column), vec![]),
244 Condition::And(conditions) => {
245 let mut sql_parts = Vec::new();
246 let mut all_params = Vec::new();
247 for cond in conditions {
248 let (sql, params) = cond.build_mysql();
249 sql_parts.push(format!("({})", sql));
250 all_params.extend(params);
251 }
252 (sql_parts.join(" AND "), all_params)
253 }
254 Condition::Or(conditions) => {
255 let mut sql_parts = Vec::new();
256 let mut all_params = Vec::new();
257 for cond in conditions {
258 let (sql, params) = cond.build_mysql();
259 sql_parts.push(format!("({})", sql));
260 all_params.extend(params);
261 }
262 (sql_parts.join(" OR "), all_params)
263 }
264 Condition::Not(cond) => {
265 let (sql, params) = cond.build_mysql();
266 (format!("NOT ({})", sql), params)
267 }
268 Condition::Raw { sql, params } => {
269 let mysql_params: Vec<MySqlValue> = params.iter().map(|v| from_wae_to_mysql(v.clone())).collect();
270 (sql.clone(), mysql_params)
271 }
272 }
273 }
274
275 #[cfg(feature = "postgres")]
276 pub(crate) fn build_postgres(&self) -> (String, Vec<PostgresParam>) {
278 let param_index = 1;
279 match self {
280 Condition::Eq { column, value } => {
281 let sql = format!("{} = ${}", column, param_index);
282 let params = vec![value_to_postgres_param(value.clone())];
283 (sql, params)
284 }
285 Condition::Ne { column, value } => {
286 let sql = format!("{} != ${}", column, param_index);
287 let params = vec![value_to_postgres_param(value.clone())];
288 (sql, params)
289 }
290 Condition::Gt { column, value } => {
291 let sql = format!("{} > ${}", column, param_index);
292 let params = vec![value_to_postgres_param(value.clone())];
293 (sql, params)
294 }
295 Condition::Gte { column, value } => {
296 let sql = format!("{} >= ${}", column, param_index);
297 let params = vec![value_to_postgres_param(value.clone())];
298 (sql, params)
299 }
300 Condition::Lt { column, value } => {
301 let sql = format!("{} < ${}", column, param_index);
302 let params = vec![value_to_postgres_param(value.clone())];
303 (sql, params)
304 }
305 Condition::Lte { column, value } => {
306 let sql = format!("{} <= ${}", column, param_index);
307 let params = vec![value_to_postgres_param(value.clone())];
308 (sql, params)
309 }
310 Condition::Like { column, pattern } => {
311 let sql = format!("{} LIKE ${}", column, param_index);
312 let params = vec![value_to_postgres_param(Value::String(pattern.clone()))];
313 (sql, params)
314 }
315 Condition::In { column, values } => {
316 let placeholders: Vec<String> = (0..values.len()).map(|i| format!("${}", param_index + i)).collect();
317 let postgres_params: Vec<PostgresParam> = values.iter().map(|v| value_to_postgres_param(v.clone())).collect();
318 (format!("{} IN ({})", column, placeholders.join(", ")), postgres_params)
319 }
320 Condition::IsNull { column } => (format!("{} IS NULL", column), vec![]),
321 Condition::IsNotNull { column } => (format!("{} IS NOT NULL", column), vec![]),
322 Condition::And(conditions) => {
323 let mut sql_parts = Vec::new();
324 let mut all_params = Vec::new();
325 let mut current_index = 1;
326 for cond in conditions {
327 let (sql, params) = cond.build_postgres();
328 let sql = replace_placeholders(&sql, current_index);
329 sql_parts.push(format!("({})", sql));
330 let params_len = params.len();
331 all_params.extend(params);
332 current_index += params_len;
333 }
334 (sql_parts.join(" AND "), all_params)
335 }
336 Condition::Or(conditions) => {
337 let mut sql_parts = Vec::new();
338 let mut all_params = Vec::new();
339 let mut current_index = 1;
340 for cond in conditions {
341 let (sql, params) = cond.build_postgres();
342 let sql = replace_placeholders(&sql, current_index);
343 sql_parts.push(format!("({})", sql));
344 let params_len = params.len();
345 all_params.extend(params);
346 current_index += params_len;
347 }
348 (sql_parts.join(" OR "), all_params)
349 }
350 Condition::Not(cond) => {
351 let (sql, params) = cond.build_postgres();
352 (format!("NOT ({})", sql), params)
353 }
354 Condition::Raw { sql, params } => {
355 let postgres_params: Vec<PostgresParam> = params.iter().map(|v| value_to_postgres_param(v.clone())).collect();
356 (sql.clone(), postgres_params)
357 }
358 }
359 }
360}
361
362#[cfg(feature = "postgres")]
363fn replace_placeholders(sql: &str, start_index: usize) -> String {
364 let mut result = String::new();
365 let mut chars = sql.chars().peekable();
366 while let Some(c) = chars.next() {
367 if c == '$' {
368 let mut num_str = String::new();
369 while let Some(&next_c) = chars.peek() {
370 if next_c.is_ascii_digit() {
371 num_str.push(next_c);
372 chars.next();
373 }
374 else {
375 break;
376 }
377 }
378 if !num_str.is_empty() {
379 if let Ok(num) = num_str.parse::<usize>() {
380 result.push_str(&format!("${}", num + start_index - 1));
381 }
382 }
383 else {
384 result.push(c);
385 }
386 }
387 else {
388 result.push(c);
389 }
390 }
391 result
392}
393
394#[derive(Debug, Clone, Copy)]
396pub enum Order {
397 Asc,
399 Desc,
401}