1pub trait SqlQuote<OUT>
3where
4 OUT: std::fmt::Display,
5{
6 fn sql_quote(&self) -> OUT;
7}
8
9pub struct SqlExpr<T: std::fmt::Display>(pub T);
11impl<T: std::fmt::Display> SqlQuote<String> for SqlExpr<T> {
12 fn sql_quote(&self) -> String {
13 format!("{}", &self.0)
14 }
15}
16macro_rules! array_join_to_str {
17 ($self:expr) => {
18 $self
19 .into_iter()
20 .map(|e| format!("{}", e.sql_quote()))
21 .collect::<Vec<String>>()
22 .join(",")
23 };
24}
25macro_rules! option_to_str {
26 ($self:expr) => {
27 match $self {
28 Some(str) => str.sql_quote().to_string(),
29 None => "NULL".to_string(),
30 }
31 };
32}
33macro_rules! sql_quote_self {
35 ($in_type:ty) => {
36 impl SqlQuote<$in_type> for $in_type {
37 fn sql_quote(&self) -> $in_type {
38 *self
39 }
40 }
41 };
42}
43sql_quote_self!(i8);
44sql_quote_self!(i16);
45sql_quote_self!(i32);
46sql_quote_self!(i64);
47sql_quote_self!(i128);
48sql_quote_self!(u8);
49sql_quote_self!(u16);
50sql_quote_self!(u32);
51sql_quote_self!(u64);
52sql_quote_self!(u128);
53sql_quote_self!(f32);
54sql_quote_self!(f64);
55sql_quote_self!(usize);
56sql_quote_self!(isize);
57impl SqlQuote<String> for char {
58 fn sql_quote(&self) -> String {
59 if (*self) == '\'' {
60 "'\\''".to_string()
61 } else {
62 format!("'{self}'")
63 }
64 }
65}
66impl SqlQuote<u8> for bool {
67 fn sql_quote(&self) -> u8 {
68 (*self) as u8
69 }
70}
71impl SqlQuote<String> for &str {
72 fn sql_quote(&self) -> String {
73 format!("'{}'", self.replace('\'', "\\'"))
74 }
75}
76impl SqlQuote<String> for String {
77 fn sql_quote(&self) -> String {
78 format!("'{}'", self.replace('\'', "\\'"))
79 }
80}
81macro_rules! sql_quote_option {
83 ($in_type:ty) => {
84 impl SqlQuote<String> for Option<$in_type> {
85 fn sql_quote(&self) -> String {
86 option_to_str!(self)
87 }
88 }
89 };
90}
91sql_quote_option!(i8);
92sql_quote_option!(i16);
93sql_quote_option!(i32);
94sql_quote_option!(i64);
95sql_quote_option!(i128);
96sql_quote_option!(u8);
97sql_quote_option!(u16);
98sql_quote_option!(u32);
99sql_quote_option!(u64);
100sql_quote_option!(u128);
101sql_quote_option!(f32);
102sql_quote_option!(f64);
103sql_quote_option!(usize);
104sql_quote_option!(isize);
105sql_quote_option!(bool);
106sql_quote_option!(char);
107sql_quote_option!(String);
108sql_quote_option!(&i8);
109sql_quote_option!(&i16);
110sql_quote_option!(&i32);
111sql_quote_option!(&i64);
112sql_quote_option!(&i128);
113sql_quote_option!(&u8);
114sql_quote_option!(&u16);
115sql_quote_option!(&u32);
116sql_quote_option!(&u64);
117sql_quote_option!(&u128);
118sql_quote_option!(&f32);
119sql_quote_option!(&f64);
120sql_quote_option!(&usize);
121sql_quote_option!(&isize);
122sql_quote_option!(&bool);
123sql_quote_option!(&char);
124sql_quote_option!(&String);
125sql_quote_option!(&str);
126
127macro_rules! sql_quote_array {
129 ($in_type:ty) => {
130 impl SqlQuote<String> for Vec<$in_type> {
131 fn sql_quote(&self) -> String {
132 array_join_to_str!(self)
133 }
134 }
135 impl SqlQuote<String> for &Vec<$in_type> {
136 fn sql_quote(&self) -> String {
137 array_join_to_str!(self)
138 }
139 }
140 impl SqlQuote<String> for [$in_type] {
141 fn sql_quote(&self) -> String {
142 array_join_to_str!(self)
143 }
144 }
145 impl SqlQuote<String> for &[$in_type] {
146 fn sql_quote(&self) -> String {
147 array_join_to_str!(self)
148 }
149 }
150 };
151}
152sql_quote_array!(i8);
153sql_quote_array!(i16);
154sql_quote_array!(i32);
155sql_quote_array!(i64);
156sql_quote_array!(i128);
157sql_quote_array!(u8);
158sql_quote_array!(u16);
159sql_quote_array!(u32);
160sql_quote_array!(u64);
161sql_quote_array!(u128);
162sql_quote_array!(f32);
163sql_quote_array!(f64);
164sql_quote_array!(usize);
165sql_quote_array!(isize);
166sql_quote_array!(bool);
167sql_quote_array!(&str);
168sql_quote_array!(String);
169
170#[macro_export]
171macro_rules! sql_format {
173 ($fmt:expr) => {
174 format!($fmt)
175 };
176 ($fmt:expr,$($argsname:tt=$argsval:expr),+$(,)?) => {
177 format!($fmt,$($argsname=$argsval.sql_quote()) ,+)
178 };
179 ($fmt:expr,$($args:expr),+$(,)?) => {
180 format!($fmt,$($args.sql_quote()),+)
181 };
182}
183#[macro_export]
184macro_rules! sql_array_str {
187 ($fmt:expr,$val:expr) => {
188 if $val.len() == 0 {
189 $crate::SqlExpr("".to_string())
190 } else {
191 $crate::SqlExpr(format!($fmt, $val.sql_quote()))
192 }
193 };
194}
195#[macro_export]
196macro_rules! sql_option_str {
198 ($some_fmt:expr,$none_fmt:expr,$val:expr) => {
199 if $val.is_none() {
200 $crate::SqlExpr(format!($none_fmt, $val.sql_quote()))
201 } else {
202 $crate::SqlExpr(format!($some_fmt, $val.sql_quote()))
203 }
204 };
205}
206
207#[test]
208fn test_sql_format_macro() {
209 assert_eq!(sql_format!("{var_i32}", var_i32 = 1), "1");
210 assert_eq!(sql_format!("{}", 1_i8), "1");
211
212 let aa = || 1;
213 assert_eq!(sql_format!("{}", aa()), "1");
214 fn bb() -> i8 {
215 1
216 }
217 assert_eq!(sql_format!("{}", bb()), "1");
218
219 assert_eq!(sql_format!("{}", "1'1'1"), "'1\\'1\\'1'");
220 assert_eq!(sql_format!("{}", "1'1'1".to_string()), "'1\\'1\\'1'");
221
222 assert_eq!(sql_format!("{}", '\''), "'\\''");
223
224 assert_eq!(
225 sql_format!("{}", vec!["1", "2'2'2'2'"]),
226 "'1','2\\'2\\'2\\'2\\''"
227 );
228
229 assert_eq!(
230 sql_format!("{}", ["1", "2'2'2'2'"]),
231 "'1','2\\'2\\'2\\'2\\''"
232 );
233
234 assert_eq!(
235 sql_format!("{}", ["1".to_string(), "2'2'2'2'".to_string()]),
236 "'1','2\\'2\\'2\\'2\\''"
237 );
238
239 assert_eq!(
240 sql_format!("{}", vec!["1".to_string(), "2'2'2'2'".to_string()]),
241 "'1','2\\'2\\'2\\'2\\''"
242 );
243
244 let a: [i8; 0] = [];
245 assert_eq!(sql_format!("{}", sql_array_str!("ddd in ({})", a)), "");
246
247 assert_eq!(
248 sql_format!("{}", sql_array_str!("ddd in ({})", [1, 2])),
249 "ddd in (1,2)"
250 );
251
252 assert_eq!(
253 sql_format!("{}", sql_option_str!("a = {}", "{}", Some(1))),
254 "a = 1"
255 );
256
257 let i: Option<i128> = None;
258 assert_eq!(
259 sql_format!("{}", sql_option_str!("{}", "a is {}", i)),
260 "a is NULL"
261 );
262
263 assert_eq!(sql_format!("{}", SqlExpr("select 1 as a")), "select 1 as a");
264}