114 lines
3.1 KiB
Rust
114 lines
3.1 KiB
Rust
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<String>,
|
|
condition: Option<Condition>,
|
|
order_by: Vec<String>,
|
|
limit: Option<usize>,
|
|
offset: Option<usize>,
|
|
}
|
|
|
|
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, <Postgres as sqlx::Database>::Row>,
|
|
>(
|
|
self,
|
|
) -> Option<T> {
|
|
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<Value>) {
|
|
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<Value> = 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)
|
|
}
|
|
}
|