Skip to main content

ngb_sqlbuilder/
clause.rs

1use crate::{Build, SqlClause};
2use std::fmt::{Debug, Display, Formatter};
3use std::marker::PhantomData;
4use tokio_postgres::types::ToSql;
5
6// #[derive(Debug)]
7pub struct Clause<'q, T> {
8    pub(crate) sql: String,
9    pub(crate) params: Vec<&'q (dyn ToSql + Sync)>,
10    _ph: PhantomData<T>,
11}
12
13pub trait ToClause<'q> {
14    fn to_clause<C>(self) -> Clause<'q, C>;
15}
16impl<'q> ToClause<'q> for &str {
17    fn to_clause<C>(self) -> Clause<'q, C> {
18        Clause::new(String::from(self), vec![])
19    }
20}
21impl<T> Clone for Clause<'_, T> {
22    fn clone(&self) -> Self {
23        Self {
24            sql: self.sql.clone(),
25            params: self.params.clone(),
26            _ph: PhantomData,
27        }
28    }
29}
30impl<'q, T> Clause<'q, T> {
31    pub(crate) fn new(sql: String, params: Vec<&'q (dyn ToSql + Sync)>) -> Self {
32        Self {
33            sql,
34            params,
35            _ph: PhantomData,
36        }
37    }
38    // pub fn alias(mut self, name: &str) -> Self {
39    //     self.sql.push_str(format!(" as \"{}\"", name).as_str());
40    //     self
41    // }
42
43    pub fn this(self) -> Self {
44        self
45    }
46
47    /// Force convert clause type
48    pub fn into<C>(self) -> Clause<'q, C> {
49        Clause::new(self.sql, self.params)
50    }
51    pub fn combine_raw(self, raw_sql: &str, params: &[&'q (dyn ToSql + Sync)]) -> Self {
52        let (mut s, mut p) = self.unwrap();
53        s.push(' ');
54        s.push_str(&raw_sql.trim());
55        p.extend_from_slice(params);
56        Self::new(s, p)
57    }
58
59    pub fn union<C>(self, clause: Clause<'q, C>) -> Clause<'q, ()> {
60        let (mut sql, mut params) = self.unwrap();
61        let (cl_sql, cl_params) = clause.unwrap();
62        sql.push_str(" UNION ");
63        sql.push_str(&cl_sql);
64        params.extend_from_slice(&cl_params);
65        Clause::new(sql, params)
66    }
67    pub fn union_all<C>(self, clause: Clause<'q, C>) -> Clause<'q, ()> {
68        let (mut sql, mut params) = self.unwrap();
69        let (cl_sql, cl_params) = clause.unwrap();
70        sql.push_str(" UNION ALL ");
71        sql.push_str(&cl_sql);
72        params.extend_from_slice(&cl_params);
73        Clause::new(sql, params)
74    }
75
76    /// This method will call `println!("{:?}", &self)` to show temporary SQL result
77    pub fn debug(self) -> Self {
78        println!("{:?}", &self);
79        self
80    }
81}
82
83// impl<T1, T2> Into<Clause<'_, T2>> for Clause<'_, T1>  {
84//     fn into(self) -> Clause<'_, T2> {
85//         todo!()
86//     }
87// }
88
89impl<'q, T> Build<'q> for Clause<'q, T> {
90    /// Build and compile query into SQL syntax with numbered parameters
91    fn build(self) -> (String, Vec<&'q (dyn ToSql + Sync)>) {
92        // let init_capacity = self.sql.len() + self.params.len();
93        // let mut result = String::with_capacity(init_capacity);
94        // let mut start = 0;
95        // let mut count = 1;
96        // for (i, _) in self.sql.match_indices("$") {
97        //     result.push_str(&self.sql[start..i]);
98        //     result.push_str(&format!("${count}"));
99        //     count += 1;
100        //     start = i + 1;
101        // }
102        // result.push_str(&self.sql[start..]);
103        (__build_sql(&self.sql, self.params.len()), self.params)
104    }
105    // fn build_ref(&self) -> (&str, &[&'q (dyn ToSql + Sync)]) {
106    //     (&self.sql, &self.params)
107    // }
108    // fn inspect(&self){
109    //     let q = __build_sql(&self.sql, 0);
110    //     println!("{}", q);
111    // }
112}
113impl<'q, T> Display for Clause<'q, T> {
114    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
115        let sql = __build_sql(&self.sql, self.params.len());
116        write!(f, "{}", &sql)
117    }
118}
119impl<'q, T> Debug for Clause<'q, T> {
120    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
121        write!(f, "{}", self)
122    }
123}
124fn __build_sql(sql: &str, params_len: usize) -> String {
125    // let init_capacity = self.sql.len() + self.params.len();
126    let mut result = String::with_capacity(sql.len() + params_len);
127    let mut start = 0;
128    let mut count = 1;
129    for (i, _) in sql.match_indices("$") {
130        result.push_str(&sql[start..i]);
131        result.push_str(&format!("${count}"));
132        count += 1;
133        start = i + 1;
134    }
135    result.push_str(&sql[start..]);
136    result
137}