use serde::Serialize;
use sqlx::FromRow;
use crate::{
queries::serialize::{
Condition, Constraint, ConstraintValue, FinalType, OrderBy, PaginateOptions, QueryData,
QueryTree,
},
utils::{placeholders, sanitize_identifier},
};
#[cfg(feature = "mysql")]
pub mod mysql;
#[cfg(feature = "postgres")]
pub mod postgres;
#[cfg(feature = "sqlite")]
pub mod sqlite;
fn prepare_sqlx_query(query: &QueryTree) -> (String, Vec<FinalType>) {
let mut string_query = "SELECT * FROM ".to_string();
let mut values = vec![];
string_query.push_str(&sanitize_identifier(&query.table));
if let Some(condition) = &query.condition {
string_query.push_str(" WHERE ");
let (placeholders, args) = condition.traverse();
string_query.push_str(&placeholders);
values.extend(args);
}
if let Some(paginate) = &query.paginate {
string_query.push_str(" ");
let pagination = paginate.traverse();
string_query.push_str(&pagination.0);
values.extend(pagination.1);
}
(string_query, values)
}
pub fn serialize_rows<T, R>(data: &QueryData<R>) -> serde_json::Value
where
T: for<'r> FromRow<'r, R> + Serialize,
R: sqlx::Row,
{
match data {
QueryData::Single(row) => match row {
Some(row) => serde_json::json!(QueryData::Single(Some(T::from_row(row).unwrap()))),
None => serde_json::json!(QueryData::Single(None::<T>)),
},
QueryData::Many(rows) => serde_json::json!(QueryData::Many(
rows.iter()
.map(|row| T::from_row(row).unwrap())
.collect::<Vec<T>>()
)),
}
}
trait Traversable {
fn traverse(&self) -> (String, Vec<FinalType>);
}
impl Traversable for FinalType {
fn traverse(&self) -> (String, Vec<FinalType>) {
("?".to_string(), vec![self.clone()])
}
}
impl Traversable for ConstraintValue {
fn traverse(&self) -> (String, Vec<FinalType>) {
match self {
ConstraintValue::List(list) => (placeholders(list.len()), list.clone()),
ConstraintValue::Final(value) => value.traverse(),
}
}
}
impl Traversable for Constraint {
fn traverse(&self) -> (String, Vec<FinalType>) {
let (values_string_query, values) = self.value.traverse();
(
format!(
"\"{}\" {} {}",
self.column, self.operator, values_string_query
),
values,
)
}
}
impl Traversable for Condition {
fn traverse(&self) -> (String, Vec<FinalType>) {
match self {
Condition::Single { constraint } => constraint.traverse(),
Condition::Or { conditions } => reduce_constraints_list(conditions, " OR "),
Condition::And { conditions } => reduce_constraints_list(conditions, " AND "),
}
}
}
impl Traversable for PaginateOptions {
fn traverse(&self) -> (String, Vec<FinalType>) {
let mut query_string = "".to_string();
let mut values: Vec<FinalType> = vec![];
if let Some(order) = &self.order_by {
query_string.push_str(
match order {
OrderBy::Asc(col) => format!("ORDER BY {} ASC ", sanitize_identifier(col)),
OrderBy::Desc(col) => format!("ORDER BY {} DESC ", sanitize_identifier(col)),
}
.as_str(),
);
} else {
query_string.push_str("ORDER BY id DESC ");
}
query_string.push_str("LIMIT ? ");
values.push(FinalType::Number(self.per_page.into()));
if let Some(offset) = self.offset {
query_string.push_str("OFFSET ? ");
values.push(FinalType::Number(offset.into()));
}
(query_string, values)
}
}
fn reduce_constraints_list(conditions: &[Condition], sep: &str) -> (String, Vec<FinalType>) {
let mut placeholder_strings: Vec<String> = vec![];
let mut total_values: Vec<FinalType> = vec![];
conditions.iter().for_each(|condition| {
let (string_query, values) = condition.traverse();
placeholder_strings.push(string_query);
total_values.extend(values);
});
(format!("({})", placeholder_strings.join(sep)), total_values)
}