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, columns: Vec, condition: Option, order_by: Vec, limit: Option, offset: Option, } impl QueryBuilder { pub fn new(table: &str) -> Self { QueryBuilder { table: table.to_string(), columns: Vec::new(), condition: None, order_by: Vec::new(), limit: None, offset: None, } } pub fn select(mut self, columns: &[&str]) -> Self { self.columns = columns.iter().map(|s| s.to_string()).collect(); self } pub fn where_condition(mut self, condition: Condition) -> Self { self.condition = Some(condition); self } pub fn order_by(mut self, column: &str, direction: &str) -> Self { self.order_by.push(format!("{} {}", column, direction)); self } pub fn limit(mut self, limit: usize) -> Self { self.limit = Some(limit); self } pub async fn fetch_optional< T: Send + Unpin + for<'r> FromRow<'r, ::Row>, >( self, ) -> Option { let (query_string, values) = self.build(); 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::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!( "Unable to fetch optional on query '{}': {}", query_string, err ); None }) } pub fn build(self) -> (String, Vec) { let columns = if self.columns.is_empty() { "*".to_string() } else { self.columns.join(",") }; let mut query = format!("SELECT {} FROM {}", columns, self.table); let mut values: Vec = Vec::new(); if let Some(condition) = self.condition { let where_condition = condition.to_sql(&mut 0); query.push_str(&format!(" WHERE {}", where_condition.0)); values = where_condition.1; } if !self.order_by.is_empty() { query.push_str(&format!(" ORDER BY {}", self.order_by.join(" ORDER BY"))); } if let Some(limit) = self.limit { query.push_str(&format!(" LIMIT {}", limit)); } if let Some(offset) = self.offset { query.push_str(&format!(" OFFSET {}", offset)); } (query, values) } }