Built insert and update builders

This commit is contained in:
2024-12-21 23:31:41 -05:00
parent 2cd2715d0d
commit 4c5300fa59
8 changed files with 390 additions and 194 deletions

View File

@@ -1,6 +1,8 @@
use std::fmt;
use std::fmt::Display;
use sqlx::{FromRow, Postgres};
use crate::data::condition::Condition;
use crate::data::Value;
pub struct QueryBuilder {
table: String,
@@ -52,16 +54,28 @@ impl QueryBuilder {
let mut query_as = sqlx::query_as(&query_string);
for value in values {
match value {
Value::INT(n) => query_as = query_as.bind(n),
Value::BIGINT(n) => query_as = query_as.bind(n),
Value::Int(n) => query_as = query_as.bind(n),
Value::OptionalInt(n) => query_as = query_as.bind(n),
Value::BigInt(n) => query_as = query_as.bind(n),
Value::OptionalBigInt(n) => query_as = query_as.bind(n),
Value::Float(n) => query_as = query_as.bind(n),
Value::OptionalFloat(n) => query_as = query_as.bind(n),
Value::Double(n) => query_as = query_as.bind(n),
Value::OptionalDouble(n) => query_as = query_as.bind(n),
Value::Bool(n) => query_as = query_as.bind(n),
Value::OptionalBool(n) => query_as = query_as.bind(n),
Value::Text(n) => query_as = query_as.bind(n),
Value::OptionalText(n) => query_as = query_as.bind(n),
}
}
let pool = crate::data::pool();
query_as.fetch_optional(pool).await.unwrap_or_else(|err| {
log::error!("{}", err);
log::error!(
"Unable to fetch optional on query '{}': {}",
query_string,
err
);
None
})
}
@@ -97,152 +111,3 @@ impl QueryBuilder {
(query, values)
}
}
#[derive(Debug, Clone)]
pub enum Value {
INT(i32),
BIGINT(i64),
Bool(bool),
Text(String),
}
impl Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::INT(n) => write!(f, "{}", n),
Value::BIGINT(n) => write!(f, "{}", n),
Value::Bool(n) => write!(f, "{}", n),
Value::Text(s) => write!(f, "'{}'", s), // Wrap strings in quotes for SQL
}
}
}
pub enum Condition {
Simple(String, Vec<Value>),
And(Box<Condition>, Box<Condition>),
Or(Box<Condition>, Box<Condition>),
Group(Box<Condition>),
}
impl Condition {
pub fn new(condition: &str) -> Self {
Condition::Simple(condition.to_string(), vec![])
}
pub fn and(self, other: Self) -> Self {
Condition::And(Box::new(self), Box::new(other))
}
pub fn or(self, other: Self) -> Self {
Condition::Or(Box::new(self), Box::new(other))
}
pub fn group(self) -> Self {
Condition::Group(Box::new(self))
}
pub fn is_equal(left: &str, right: impl Into<Value>) -> Self {
Condition::Simple(format!("{} = ?", left), vec![right.into()])
}
pub fn not_equal(left: &str, right: impl Into<Value>) -> Self {
Condition::Simple(format!("{} != ?", left), vec![right.into()])
}
pub fn is_null(value: &str) -> Self {
Condition::Simple(format!("{} IS NULL", value), vec![])
}
pub fn not_null(value: &str) -> Self {
Condition::Simple(format!("{} IS NOT NULL", value), vec![])
}
pub fn is_in(left: &str, right: Vec<Value>) -> Self {
let right_list = right
.iter()
.map(|v| "'?'".to_string())
.collect::<Vec<_>>()
.join(", ");
Condition::Simple(format!("{} IN ({})", left, right_list), right)
}
pub fn not_in(left: &str, right: Vec<Value>) -> Self {
let right_list = right
.iter()
.map(|v| "'?'".to_string())
.collect::<Vec<_>>()
.join(", ");
Condition::Simple(format!("{} NOT IN ({})", left, right_list), right)
}
pub fn like(left: &str, right: impl Into<Value>) -> Self {
Condition::Simple(format!("{} LIKE '?'", left), vec![right.into()])
}
pub fn not_like(left: &str, right: impl Into<Value>) -> Self {
Condition::Simple(format!("{} NOT LIKE '?'", left), vec![right.into()])
}
pub fn i_like(left: &str, right: impl Into<Value>) -> Self {
Condition::Simple(format!("{} ILIKE '?'", left), vec![right.into()])
}
pub fn not_i_like(left: &str, right: impl Into<Value>) -> Self {
Condition::Simple(format!("{} NOT ILIKE '?'", left), vec![right.into()])
}
pub fn gt(left: &str, right: impl Into<Value>) -> Self {
Condition::Simple(format!("{} > ?", left), vec![right.into()])
}
pub fn gte(left: &str, right: impl Into<Value>) -> Self {
Condition::Simple(format!("{} >= ?", left), vec![right.into()])
}
pub fn lt(left: &str, right: impl Into<Value>) -> Self {
Condition::Simple(format!("{} < ?", left), vec![right.into()])
}
pub fn lte(left: &str, right: impl Into<Value>) -> Self {
Condition::Simple(format!("{} <= ?", left), vec![right.into()])
}
fn to_sql(&self, mut counter: &mut usize) -> (String, Vec<Value>) {
let mut sql = String::new();
let mut binds = Vec::new();
match self {
Condition::Simple(condition, values) => {
// Replace all instances of '?' with an numbered bind
let mut bind_index = *counter;
let numbered_condition = condition.replace("?", {
bind_index += 1;
&format!("${}", bind_index)
});
sql.push_str(&numbered_condition);
binds.extend(values.clone());
}
Condition::And(left, right) => {
let (left_sql, left_binds) = left.to_sql(counter);
let (right_sql, right_binds) = right.to_sql(counter);
sql.push_str(&format!("{} AND {}", left_sql, right_sql));
binds.extend(left_binds);
binds.extend(right_binds);
}
Condition::Or(left, right) => {
let (left_sql, left_binds) = left.to_sql(counter);
let (right_sql, right_binds) = right.to_sql(counter);
sql.push_str(&format!("{} OR {}", left_sql, right_sql));
binds.extend(left_binds);
binds.extend(right_binds);
}
Condition::Group(inner) => {
let (inner_sql, inner_binds) = inner.to_sql(counter);
sql.push_str(&format!("({})", inner_sql));
binds.extend(inner_binds);
}
};
(sql, binds)
}
}