use lazy_static::lazy_static;
use sqlx::{any::AnyRow, FromRow, Row};
use crate::{get_placeholder, get_type_name, Connection};
lazy_static! {
pub static ref PLACEHOLDER: &'static str = get_placeholder().expect(
"DATABASE_URL is not set, make sur the database is 'sqlite', 'postgres' or 'mysql'"
);
}
#[derive(Debug)]
pub enum Condition {
FieldCondition {
field: String,
value: String,
value_type: String,
comparison_operator: String,
},
LogicalOperator { operator: String },
}
pub trait Or {
fn or(self, conditions: Vec<Condition>) -> Vec<Condition>;
}
pub trait And {
fn and(self, conditions: Vec<Condition>) -> Vec<Condition>;
}
impl Or for Vec<Condition> {
fn or(mut self, conditions: Vec<Condition>) -> Vec<Condition> {
self.push(Condition::LogicalOperator {
operator: "or".to_string(),
});
self.extend(conditions);
self
}
}
impl And for Vec<Condition> {
fn and(mut self, conditions: Vec<Condition>) -> Vec<Condition> {
self.push(Condition::LogicalOperator {
operator: "and".to_string(),
});
self.extend(conditions);
self
}
}
pub trait Query {
fn to_update_query(&self) -> (String, Vec<(String, String)>);
fn to_select_query(&self) -> (String, Vec<(String, String)>);
fn to_insert_query(&self) -> (String, String, Vec<(String, String)>);
}
impl Query for Vec<Condition> {
fn to_update_query(&self) -> (String, Vec<(String, String)>) {
let mut args = Vec::new();
let mut placeholders = Vec::new();
let mut index = 0;
for condition in self {
if let Condition::FieldCondition {
field,
value,
value_type,
#[allow(unused_variables)]
comparison_operator,
} = condition
{
index += 1;
args.push((value.clone(), value_type.clone()));
let placeholder = PLACEHOLDER.to_string();
placeholders.push(format!("{field}={placeholder}{index}",));
}
}
(placeholders.join(", "), args)
}
fn to_select_query(&self) -> (String, Vec<(String, String)>) {
let mut args = Vec::new();
let mut placeholders = Vec::new();
let mut index = 0;
for condition in self {
match condition {
Condition::FieldCondition {
field,
value,
value_type,
comparison_operator,
} => {
index += 1;
args.push((value.clone(), value_type.clone()));
let placeholder = PLACEHOLDER.to_string();
placeholders.push(format!("{field}{comparison_operator}{placeholder}{index}",));
}
Condition::LogicalOperator { operator } => {
placeholders.push(operator.to_owned());
}
}
}
(placeholders.join(" "), args)
}
fn to_insert_query(&self) -> (String, String, Vec<(String, String)>) {
let mut args = Vec::new();
let mut fields = Vec::new();
let mut placeholders = Vec::new();
let mut index = 0;
for condition in self {
if let Condition::FieldCondition {
field,
value,
value_type,
..
} = condition
{
index += 1;
args.push((value.clone(), value_type.clone()));
fields.push(field.clone());
let placeholder = PLACEHOLDER.to_string();
placeholders.push(format!("{placeholder}{index}"));
}
}
(fields.join(", "), placeholders.join(", "), args)
}
}
#[async_trait::async_trait]
pub trait Model {
const SCHEMA: &'static str;
const NAME: &'static str;
const PK: &'static str;
async fn migrate(conn: &Connection) -> bool
where
Self: Sized,
{
println!("{:?}", Self::SCHEMA);
if let Err(err) = sqlx::query(Self::SCHEMA).execute(conn).await {
eprintln!("Error during the migration\n->{err}");
false
} else {
true
}
}
async fn save(&self, conn: &Connection) -> bool
where
Self: Sized;
async fn create(kw: Vec<Condition>, conn: &Connection) -> bool
where
Self: Sized,
{
let (fields, placeholders, args) = kw.to_insert_query();
let query = format!(
"insert into {table_name} ({fields}) values ({placeholders});",
table_name = Self::NAME
);
let mut stream = sqlx::query(&query);
binds!(args, stream);
stream.execute(conn).await.is_ok()
}
async fn update(&self, conn: &Connection) -> bool
where
Self: Sized;
async fn set<T: ToString + Clone + Send + Sync>(
id_value: T,
kw: Vec<Condition>,
conn: &Connection,
) -> bool {
let (placeholders, mut args) = kw.to_update_query();
args.push((
id_value.clone().to_string(),
get_type_name(id_value.clone()).to_string(),
));
let index_id = args.len();
let placeholder = PLACEHOLDER.to_string();
let query = format!(
"update {table_name} set {placeholders} where {id}={placeholder}{index_id};",
id = Self::PK,
table_name = Self::NAME,
);
let mut stream = sqlx::query(&query);
binds!(args, stream);
stream.execute(conn).await.is_ok()
}
async fn delete(&self, conn: &Connection) -> bool
where
Self: Sized;
async fn all(conn: &Connection) -> Vec<Self>
where
Self: Sized + Unpin + for<'r> FromRow<'r, AnyRow> + Clone,
{
let query = format!("select * from {table_name}", table_name = Self::NAME);
sqlx::query_as::<_, Self>(&query)
.fetch_all(conn)
.await
.unwrap_or_default()
}
async fn filter(kw: Vec<Condition>, conn: &Connection) -> Vec<Self>
where
Self: Sized + Unpin + for<'r> FromRow<'r, AnyRow> + Clone,
{
let (fields, args) = kw.to_select_query();
let query = format!(
"SELECT * FROM {table_name} WHERE {fields};",
table_name = Self::NAME
);
let mut stream = sqlx::query_as::<_, Self>(&query);
binds!(args, stream);
stream.fetch_all(conn).await.unwrap_or_default()
}
async fn get(kw: Vec<Condition>, conn: &Connection) -> Option<Self>
where
Self: Sized + Unpin + for<'r> FromRow<'r, AnyRow> + Clone,
{
Self::filter(kw, conn).await.first().cloned()
}
async fn count(&self, conn: &Connection) -> i64
where
Self: Sized,
{
let query = format!("select count(*) from {table_name}", table_name = Self::NAME);
sqlx::query(query.as_str())
.fetch_one(conn)
.await
.map_or(0, |r| r.get(0))
}
}
#[async_trait::async_trait]
pub trait Delete {
async fn delete(&self, conn: &Connection) -> bool;
}
#[async_trait::async_trait]
impl<T> Delete for Vec<T>
where
T: Model + Sync,
{
async fn delete(&self, conn: &Connection) -> bool {
let query = format!("delete from {table_name}", table_name = T::NAME);
sqlx::query(query.as_str()).execute(conn).await.is_ok()
}
}