use wae_types::Value;
#[cfg(feature = "limbo")]
use crate::types::from_wae_value;
#[cfg(feature = "limbo")]
use limbo::Value as LimboValue;
#[cfg(feature = "mysql")]
use crate::types::from_wae_to_mysql;
#[cfg(feature = "mysql")]
use mysql_async::Value as MySqlValue;
#[cfg(feature = "postgres")]
use crate::connection::postgres::{PostgresParam, value_to_postgres_param};
#[derive(Debug, Clone)]
pub enum Condition {
Eq {
column: String,
value: Value,
},
Ne {
column: String,
value: Value,
},
Gt {
column: String,
value: Value,
},
Gte {
column: String,
value: Value,
},
Lt {
column: String,
value: Value,
},
Lte {
column: String,
value: Value,
},
Like {
column: String,
pattern: String,
},
In {
column: String,
values: Vec<Value>,
},
IsNull {
column: String,
},
IsNotNull {
column: String,
},
And(Vec<Condition>),
Or(Vec<Condition>),
Not(Box<Condition>),
Raw {
sql: String,
params: Vec<Value>,
},
}
impl Condition {
pub fn eq<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
Condition::Eq { column: column.into(), value: value.into() }
}
pub fn ne<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
Condition::Ne { column: column.into(), value: value.into() }
}
pub fn gt<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
Condition::Gt { column: column.into(), value: value.into() }
}
pub fn gte<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
Condition::Gte { column: column.into(), value: value.into() }
}
pub fn lt<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
Condition::Lt { column: column.into(), value: value.into() }
}
pub fn lte<C: Into<String>, V: Into<Value>>(column: C, value: V) -> Self {
Condition::Lte { column: column.into(), value: value.into() }
}
pub fn like<C: Into<String>, P: Into<String>>(column: C, pattern: P) -> Self {
Condition::Like { column: column.into(), pattern: pattern.into() }
}
pub fn in_<C: Into<String>, V: Into<Value>>(column: C, values: Vec<V>) -> Self {
Condition::In { column: column.into(), values: values.into_iter().map(|v| v.into()).collect() }
}
pub fn is_null<C: Into<String>>(column: C) -> Self {
Condition::IsNull { column: column.into() }
}
pub fn is_not_null<C: Into<String>>(column: C) -> Self {
Condition::IsNotNull { column: column.into() }
}
pub fn and(conditions: Vec<Condition>) -> Self {
Condition::And(conditions)
}
pub fn or(conditions: Vec<Condition>) -> Self {
Condition::Or(conditions)
}
pub fn negate(condition: Condition) -> Self {
Condition::Not(Box::new(condition))
}
pub fn raw<S: Into<String>>(sql: S, params: Vec<Value>) -> Self {
Condition::Raw { sql: sql.into(), params }
}
#[cfg(feature = "limbo")]
pub(crate) fn build_limbo(&self) -> (String, Vec<LimboValue>) {
match self {
Condition::Eq { column, value } => (format!("{} = ?", column), vec![from_wae_value(value.clone())]),
Condition::Ne { column, value } => (format!("{} != ?", column), vec![from_wae_value(value.clone())]),
Condition::Gt { column, value } => (format!("{} > ?", column), vec![from_wae_value(value.clone())]),
Condition::Gte { column, value } => (format!("{} >= ?", column), vec![from_wae_value(value.clone())]),
Condition::Lt { column, value } => (format!("{} < ?", column), vec![from_wae_value(value.clone())]),
Condition::Lte { column, value } => (format!("{} <= ?", column), vec![from_wae_value(value.clone())]),
Condition::Like { column, pattern } => (format!("{} LIKE ?", column), vec![LimboValue::Text(pattern.clone())]),
Condition::In { column, values } => {
let placeholders: Vec<&str> = values.iter().map(|_| "?").collect();
let limbo_values: Vec<LimboValue> = values.iter().map(|v| from_wae_value(v.clone())).collect();
(format!("{} IN ({})", column, placeholders.join(", ")), limbo_values)
}
Condition::IsNull { column } => (format!("{} IS NULL", column), vec![]),
Condition::IsNotNull { column } => (format!("{} IS NOT NULL", column), vec![]),
Condition::And(conditions) => {
let mut sql_parts = Vec::new();
let mut all_params = Vec::new();
for cond in conditions {
let (sql, params) = cond.build_limbo();
sql_parts.push(format!("({})", sql));
all_params.extend(params);
}
(sql_parts.join(" AND "), all_params)
}
Condition::Or(conditions) => {
let mut sql_parts = Vec::new();
let mut all_params = Vec::new();
for cond in conditions {
let (sql, params) = cond.build_limbo();
sql_parts.push(format!("({})", sql));
all_params.extend(params);
}
(sql_parts.join(" OR "), all_params)
}
Condition::Not(cond) => {
let (sql, params) = cond.build_limbo();
(format!("NOT ({})", sql), params)
}
Condition::Raw { sql, params } => {
let limbo_params: Vec<LimboValue> = params.iter().map(|v| from_wae_value(v.clone())).collect();
(sql.clone(), limbo_params)
}
}
}
#[cfg(feature = "mysql")]
pub(crate) fn build_mysql(&self) -> (String, Vec<MySqlValue>) {
match self {
Condition::Eq { column, value } => (format!("{} = ?", column), vec![from_wae_to_mysql(value.clone())]),
Condition::Ne { column, value } => (format!("{} != ?", column), vec![from_wae_to_mysql(value.clone())]),
Condition::Gt { column, value } => (format!("{} > ?", column), vec![from_wae_to_mysql(value.clone())]),
Condition::Gte { column, value } => (format!("{} >= ?", column), vec![from_wae_to_mysql(value.clone())]),
Condition::Lt { column, value } => (format!("{} < ?", column), vec![from_wae_to_mysql(value.clone())]),
Condition::Lte { column, value } => (format!("{} <= ?", column), vec![from_wae_to_mysql(value.clone())]),
Condition::Like { column, pattern } => {
(format!("{} LIKE ?", column), vec![MySqlValue::Bytes(pattern.clone().into_bytes())])
}
Condition::In { column, values } => {
let placeholders: Vec<&str> = values.iter().map(|_| "?").collect();
let mysql_values: Vec<MySqlValue> = values.iter().map(|v| from_wae_to_mysql(v.clone())).collect();
(format!("{} IN ({})", column, placeholders.join(", ")), mysql_values)
}
Condition::IsNull { column } => (format!("{} IS NULL", column), vec![]),
Condition::IsNotNull { column } => (format!("{} IS NOT NULL", column), vec![]),
Condition::And(conditions) => {
let mut sql_parts = Vec::new();
let mut all_params = Vec::new();
for cond in conditions {
let (sql, params) = cond.build_mysql();
sql_parts.push(format!("({})", sql));
all_params.extend(params);
}
(sql_parts.join(" AND "), all_params)
}
Condition::Or(conditions) => {
let mut sql_parts = Vec::new();
let mut all_params = Vec::new();
for cond in conditions {
let (sql, params) = cond.build_mysql();
sql_parts.push(format!("({})", sql));
all_params.extend(params);
}
(sql_parts.join(" OR "), all_params)
}
Condition::Not(cond) => {
let (sql, params) = cond.build_mysql();
(format!("NOT ({})", sql), params)
}
Condition::Raw { sql, params } => {
let mysql_params: Vec<MySqlValue> = params.iter().map(|v| from_wae_to_mysql(v.clone())).collect();
(sql.clone(), mysql_params)
}
}
}
#[cfg(feature = "postgres")]
pub(crate) fn build_postgres(&self) -> (String, Vec<PostgresParam>) {
let param_index = 1;
match self {
Condition::Eq { column, value } => {
let sql = format!("{} = ${}", column, param_index);
let params = vec![value_to_postgres_param(value.clone())];
(sql, params)
}
Condition::Ne { column, value } => {
let sql = format!("{} != ${}", column, param_index);
let params = vec![value_to_postgres_param(value.clone())];
(sql, params)
}
Condition::Gt { column, value } => {
let sql = format!("{} > ${}", column, param_index);
let params = vec![value_to_postgres_param(value.clone())];
(sql, params)
}
Condition::Gte { column, value } => {
let sql = format!("{} >= ${}", column, param_index);
let params = vec![value_to_postgres_param(value.clone())];
(sql, params)
}
Condition::Lt { column, value } => {
let sql = format!("{} < ${}", column, param_index);
let params = vec![value_to_postgres_param(value.clone())];
(sql, params)
}
Condition::Lte { column, value } => {
let sql = format!("{} <= ${}", column, param_index);
let params = vec![value_to_postgres_param(value.clone())];
(sql, params)
}
Condition::Like { column, pattern } => {
let sql = format!("{} LIKE ${}", column, param_index);
let params = vec![value_to_postgres_param(Value::String(pattern.clone()))];
(sql, params)
}
Condition::In { column, values } => {
let placeholders: Vec<String> = (0..values.len()).map(|i| format!("${}", param_index + i)).collect();
let postgres_params: Vec<PostgresParam> = values.iter().map(|v| value_to_postgres_param(v.clone())).collect();
(format!("{} IN ({})", column, placeholders.join(", ")), postgres_params)
}
Condition::IsNull { column } => (format!("{} IS NULL", column), vec![]),
Condition::IsNotNull { column } => (format!("{} IS NOT NULL", column), vec![]),
Condition::And(conditions) => {
let mut sql_parts = Vec::new();
let mut all_params = Vec::new();
let mut current_index = 1;
for cond in conditions {
let (sql, params) = cond.build_postgres();
let sql = replace_placeholders(&sql, current_index);
sql_parts.push(format!("({})", sql));
let params_len = params.len();
all_params.extend(params);
current_index += params_len;
}
(sql_parts.join(" AND "), all_params)
}
Condition::Or(conditions) => {
let mut sql_parts = Vec::new();
let mut all_params = Vec::new();
let mut current_index = 1;
for cond in conditions {
let (sql, params) = cond.build_postgres();
let sql = replace_placeholders(&sql, current_index);
sql_parts.push(format!("({})", sql));
let params_len = params.len();
all_params.extend(params);
current_index += params_len;
}
(sql_parts.join(" OR "), all_params)
}
Condition::Not(cond) => {
let (sql, params) = cond.build_postgres();
(format!("NOT ({})", sql), params)
}
Condition::Raw { sql, params } => {
let postgres_params: Vec<PostgresParam> = params.iter().map(|v| value_to_postgres_param(v.clone())).collect();
(sql.clone(), postgres_params)
}
}
}
}
#[cfg(feature = "postgres")]
fn replace_placeholders(sql: &str, start_index: usize) -> String {
let mut result = String::new();
let mut chars = sql.chars().peekable();
while let Some(c) = chars.next() {
if c == '$' {
let mut num_str = String::new();
while let Some(&next_c) = chars.peek() {
if next_c.is_ascii_digit() {
num_str.push(next_c);
chars.next();
}
else {
break;
}
}
if !num_str.is_empty() {
if let Ok(num) = num_str.parse::<usize>() {
result.push_str(&format!("${}", num + start_index - 1));
}
}
else {
result.push(c);
}
}
else {
result.push(c);
}
}
result
}
#[derive(Debug, Clone, Copy)]
pub enum Order {
Asc,
Desc,
}