1
2
3use std::sync::Arc;
4use std::collections::BTreeMap;
5
6use tokio_postgres::types::ToSql;
7
8pub struct SqlBuilder {
9
10 pub statement_base: SqlStatementBase,
11 pub table_name : String,
12 pub where_params: BTreeMap<String, Arc<dyn ToSql + Sync> > ,
13
14 pub order: Option<(String,OrderingDirection)> ,
15
16 pub limit: Option< u32 >,
17
18
19}
20
21impl SqlBuilder {
22 pub fn build(&self) -> (String , Vec<Arc<dyn ToSql + Sync>> ) {
23 let mut query = format!("{} FROM {}", self.statement_base.build(), self.table_name);
24 let mut conditions = Vec::new();
25 let mut params: Vec<Arc<dyn ToSql + Sync>> = Vec::new();
26
27 for (key, param) in &self.where_params {
29 params.push(Arc::clone(param)); conditions.push(format!("{} = ${}", key, params.len()));
31 }
32
33 if !conditions.is_empty() {
34 query.push_str(" WHERE ");
35 query.push_str(&conditions.join(" AND "));
36 }
37
38 if let Some((column, direction)) = &self.order {
40 query.push_str(&format!(" ORDER BY {} {}", column, direction.build()));
41 }
42
43 if let Some(limit) = self.limit {
45 query.push_str(&format!(" LIMIT {}", limit));
46 }
47
48 ( query , params)
49 }
50}
51
52
53
54pub enum SqlStatementBase {
55 SelectAll,
56}
57
58impl SqlStatementBase {
59
60 pub fn build(&self) -> String {
61
62 match self {
63
64 Self::SelectAll => "SELECT *"
65
66 }.to_string()
67 }
68
69}
70
71pub enum OrderingDirection {
72
73 DESC,
74 ASC
75}
76
77
78impl OrderingDirection {
79
80 pub fn build(&self) -> String {
81
82 match self {
83
84 Self::DESC => "DESC" ,
85 Self::ASC => "ASC"
86
87 }.to_string()
88 }
89
90}
91
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use std::collections::HashMap;
97 use std::sync::Arc;
98 #[test]
101 fn test_sql_builder() {
102 let mut where_params: HashMap<String, Arc<dyn ToSql + Sync>> = HashMap::new();
103 where_params.insert("chain_id".to_string(), Arc::new(1_i64));
104 where_params.insert("status".to_string(), Arc::new("active".to_string()));
105
106 let sql_builder = SqlBuilder {
107 statement_base: SqlStatementBase::SelectAll,
108 table_name: "teller_bids".to_string(),
109 where_params,
110 order: Some(("created_at".to_string(), OrderingDirection::DESC)),
111 limit: Some(10),
112 };
113
114 let (query,params) = sql_builder.build();
115
116 assert_eq!(
117 query,
118 "SELECT * FROM teller_bids WHERE chain_id = $1 AND status = $2 ORDER BY created_at DESC LIMIT 10"
119 );
120
121 assert_eq!(
122 params.len(),
123 2
124 );
125 }
126}