1use crate::{error::FbError, ibase, SqlType};
4use regex::{Captures, Regex};
5use std::collections::HashMap;
6
7pub use SqlType::*;
8
9pub const MAX_TEXT_LENGTH: usize = 32767;
11
12impl SqlType {
13 pub fn sql_type_and_subtype(&self) -> (u32, u32) {
15 match self {
16 Text(s) => {
17 if s.len() > MAX_TEXT_LENGTH {
18 (ibase::SQL_BLOB + 1, 1)
19 } else {
20 (ibase::SQL_TEXT + 1, 0)
21 }
22 }
23 Integer(_) => (ibase::SQL_INT64 + 1, 0),
24 Floating(_) => (ibase::SQL_DOUBLE + 1, 0),
25 Timestamp(_) => (ibase::SQL_TIMESTAMP + 1, 0),
26 Null => (ibase::SQL_TEXT + 1, 0),
27 Binary(_) => (ibase::SQL_BLOB + 1, 0),
28 Boolean(_) => (ibase::SQL_BOOLEAN + 1, 0),
29 }
30 }
31}
32
33pub trait IntoParam {
35 fn into_param(self) -> SqlType;
36}
37
38impl IntoParam for Vec<u8> {
39 fn into_param(self) -> SqlType {
40 Binary(self)
41 }
42}
43
44impl IntoParam for String {
45 fn into_param(self) -> SqlType {
46 Text(self)
47 }
48}
49
50impl IntoParam for i64 {
51 fn into_param(self) -> SqlType {
52 Integer(self)
53 }
54}
55
56impl IntoParam for bool {
57 fn into_param(self) -> SqlType {
58 Boolean(self)
59 }
60}
61
62macro_rules! impl_param_int {
64 ( $( $t: ident ),+ ) => {
65 $(
66 impl IntoParam for $t {
67 fn into_param(self) -> SqlType {
68 (self as i64).into_param()
69 }
70 }
71 )+
72 };
73}
74
75impl_param_int!(i32, u32, i16, u16, i8, u8);
76
77impl IntoParam for f64 {
78 fn into_param(self) -> SqlType {
79 Floating(self)
80 }
81}
82
83impl IntoParam for f32 {
84 fn into_param(self) -> SqlType {
85 (self as f64).into_param()
86 }
87}
88
89impl<T> IntoParam for Option<T>
91where
92 T: IntoParam,
93{
94 fn into_param(self) -> SqlType {
95 if let Some(v) = self {
96 v.into_param()
97 } else {
98 Null
99 }
100 }
101}
102
103impl<T, B> IntoParam for &B
105where
106 B: ToOwned<Owned = T> + ?Sized,
107 T: core::borrow::Borrow<B> + IntoParam,
108{
109 fn into_param(self) -> SqlType {
110 self.to_owned().into_param()
111 }
112}
113
114impl<T> From<T> for SqlType
116where
117 T: IntoParam,
118{
119 fn from(param: T) -> Self {
120 param.into_param()
121 }
122}
123
124pub enum ParamsType {
126 Positional(Vec<SqlType>),
130
131 Named(HashMap<String, SqlType>),
150}
151
152impl ParamsType {
153 pub fn named(&self) -> bool {
154 match self {
155 ParamsType::Positional(_) => false,
156 ParamsType::Named(_) => true,
157 }
158 }
159}
160
161#[allow(clippy::wrong_self_convention)]
162pub trait IntoParams {
169 fn to_params(self) -> ParamsType;
170}
171
172impl IntoParams for ParamsType {
173 fn to_params(self) -> ParamsType {
174 self
175 }
176}
177
178impl IntoParams for Vec<SqlType> {
181 fn to_params(self) -> ParamsType {
182 ParamsType::Positional(self)
183 }
184}
185
186impl IntoParams for () {
188 fn to_params(self) -> ParamsType {
189 ParamsType::Positional(vec![])
190 }
191}
192
193macro_rules! impl_into_params {
195 ($([$t: ident, $v: ident]),+) => {
196 impl<$($t),+> IntoParams for ($($t,)+)
197 where
198 $( $t: IntoParam, )+
199 {
200 fn to_params(self) -> ParamsType {
201 let ( $($v,)+ ) = self;
202
203 ParamsType::Positional(vec![ $(
204 $v.into_param(),
205 )+ ])
206 }
207 }
208 };
209}
210
211macro_rules! impls_into_params {
213 ([$t: ident, $v: ident]) => {
214 impl_into_params!([$t, $v]);
215 };
216
217 ([$t: ident, $v: ident], $([$ts: ident, $vs: ident]),+ ) => {
218 impls_into_params!($([$ts, $vs]),+);
219
220 impl_into_params!([$t, $v], $([$ts, $vs]),+);
221 };
222}
223
224impls_into_params!(
225 [A, a],
226 [B, b],
227 [C, c],
228 [D, d],
229 [E, e],
230 [F, f],
231 [G, g],
232 [H, h],
233 [I, i],
234 [J, j],
235 [K, k],
236 [L, l],
237 [M, m],
238 [N, n],
239 [O, o]
240);
241
242pub struct NamedParams {
246 pub sql: String,
247 params_names: Vec<String>,
248}
249
250impl NamedParams {
251 pub fn parse(raw_sql: &str) -> Result<Self, FbError> {
254 let rparams = Regex::new(r#"('[^']*')|:\w+"#)
255 .map_err(|e| FbError::from(format!("Error on start the regex for named params: {}", e)))
256 .unwrap();
257
258 let mut params_names = vec![];
259 let sql = rparams
260 .replace_all(raw_sql, |caps: &Captures| match caps.get(1) {
261 Some(same) => same.as_str().to_string(),
262 None => "?".to_string(),
263 })
264 .to_string();
265
266 for params in rparams.captures_iter(raw_sql) {
267 for param in params
268 .iter()
269 .filter_map(|p| p.map(|m| m.as_str()))
270 .filter(|p| p.starts_with(':'))
271 {
272 params_names.push(param.replace(':', ""));
273 }
274 }
275
276 Ok(NamedParams { sql, params_names })
277 }
278
279 pub fn empty(raw_sql: &str) -> Self {
281 Self {
282 sql: raw_sql.to_string(),
283 params_names: Default::default(),
284 }
285 }
286
287 pub fn convert<P>(&self, params: P) -> Result<Vec<SqlType>, FbError>
290 where
291 P: IntoParams,
292 {
293 match params.to_params() {
294 ParamsType::Named(names) => {
295 let mut new_params = vec![];
296
297 for qname in &self.params_names {
298 if let Some(param) = names.get(qname) {
299 new_params.push(param.clone());
300 } else {
301 return Err(FbError::from(format!(
302 "Param :{} not found in the provided struct",
303 qname
304 )));
305 }
306 }
307
308 Ok(new_params)
309 }
310 ParamsType::Positional(p) => Ok(p),
311 }
312 }
313}