firebolt/
types.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
4pub enum Type {
5    Int,
6    Long,
7    Float,
8    Double,
9    Decimal,
10    Text,
11    Date,
12    Timestamp,
13    TimestampTZ,
14    Boolean,
15    Array,
16    Struct,
17    Geography,
18    Bytes,
19}
20
21pub trait TypeConversion {
22    fn convert_from_json(
23        value: &serde_json::Value,
24        column_type: &Type,
25    ) -> Result<Self, crate::error::FireboltError>
26    where
27        Self: Sized;
28}
29
30impl TypeConversion for i32 {
31    fn convert_from_json(
32        value: &serde_json::Value,
33        column_type: &Type,
34    ) -> Result<Self, crate::error::FireboltError> {
35        match column_type {
36            Type::Int => {
37                if value.is_null() {
38                    return Err(crate::error::FireboltError::Serialization(
39                        "Cannot convert null to non-nullable type".to_string(),
40                    ));
41                }
42                value
43                    .as_i64()
44                    .and_then(|v| i32::try_from(v).ok())
45                    .ok_or_else(|| {
46                        crate::error::FireboltError::Serialization(
47                            "Failed to convert to i32".to_string(),
48                        )
49                    })
50            }
51            _ => Err(crate::error::FireboltError::Serialization(format!(
52                "Cannot convert {column_type:?} to i32"
53            ))),
54        }
55    }
56}
57
58impl TypeConversion for Option<i32> {
59    fn convert_from_json(
60        value: &serde_json::Value,
61        column_type: &Type,
62    ) -> Result<Self, crate::error::FireboltError> {
63        if value.is_null() {
64            return Ok(None);
65        }
66        match column_type {
67            Type::Int => {
68                let val = value
69                    .as_i64()
70                    .and_then(|v| i32::try_from(v).ok())
71                    .ok_or_else(|| {
72                        crate::error::FireboltError::Serialization(
73                            "Failed to convert to i32".to_string(),
74                        )
75                    })?;
76                Ok(Some(val))
77            }
78            _ => Err(crate::error::FireboltError::Serialization(format!(
79                "Cannot convert {column_type:?} to Option<i32>"
80            ))),
81        }
82    }
83}
84
85impl TypeConversion for num_bigint::BigInt {
86    fn convert_from_json(
87        value: &serde_json::Value,
88        column_type: &Type,
89    ) -> Result<Self, crate::error::FireboltError> {
90        match column_type {
91            Type::Long => {
92                if value.is_null() {
93                    return Err(crate::error::FireboltError::Serialization(
94                        "Cannot convert null to non-nullable type".to_string(),
95                    ));
96                }
97                if let Some(v) = value.as_i64() {
98                    Ok(num_bigint::BigInt::from(v))
99                } else if let Some(s) = value.as_str() {
100                    s.parse().map_err(|_| {
101                        crate::error::FireboltError::Serialization(
102                            "Failed to parse BigInt from string".to_string(),
103                        )
104                    })
105                } else {
106                    Err(crate::error::FireboltError::Serialization(
107                        "Failed to convert to BigInt".to_string(),
108                    ))
109                }
110            }
111            _ => Err(crate::error::FireboltError::Serialization(format!(
112                "Cannot convert {column_type:?} to BigInt"
113            ))),
114        }
115    }
116}
117
118impl TypeConversion for Option<num_bigint::BigInt> {
119    fn convert_from_json(
120        value: &serde_json::Value,
121        column_type: &Type,
122    ) -> Result<Self, crate::error::FireboltError> {
123        if value.is_null() {
124            return Ok(None);
125        }
126        match column_type {
127            Type::Long => {
128                let val = if let Some(v) = value.as_i64() {
129                    num_bigint::BigInt::from(v)
130                } else if let Some(s) = value.as_str() {
131                    s.parse().map_err(|_| {
132                        crate::error::FireboltError::Serialization(
133                            "Failed to parse BigInt from string".to_string(),
134                        )
135                    })?
136                } else {
137                    return Err(crate::error::FireboltError::Serialization(
138                        "Failed to convert to BigInt".to_string(),
139                    ));
140                };
141                Ok(Some(val))
142            }
143            _ => Err(crate::error::FireboltError::Serialization(format!(
144                "Cannot convert {column_type:?} to Option<BigInt>"
145            ))),
146        }
147    }
148}
149
150impl TypeConversion for f32 {
151    fn convert_from_json(
152        value: &serde_json::Value,
153        column_type: &Type,
154    ) -> Result<Self, crate::error::FireboltError> {
155        match column_type {
156            Type::Float => {
157                if value.is_null() {
158                    return Err(crate::error::FireboltError::Serialization(
159                        "Cannot convert null to non-nullable type".to_string(),
160                    ));
161                }
162                value.as_f64().map(|v| v as f32).ok_or_else(|| {
163                    crate::error::FireboltError::Serialization(
164                        "Failed to convert to f32".to_string(),
165                    )
166                })
167            }
168            _ => Err(crate::error::FireboltError::Serialization(format!(
169                "Cannot convert {column_type:?} to f32"
170            ))),
171        }
172    }
173}
174
175impl TypeConversion for Option<f32> {
176    fn convert_from_json(
177        value: &serde_json::Value,
178        column_type: &Type,
179    ) -> Result<Self, crate::error::FireboltError> {
180        if value.is_null() {
181            return Ok(None);
182        }
183        match column_type {
184            Type::Float => {
185                let val = value.as_f64().map(|v| v as f32).ok_or_else(|| {
186                    crate::error::FireboltError::Serialization(
187                        "Failed to convert to f32".to_string(),
188                    )
189                })?;
190                Ok(Some(val))
191            }
192            _ => Err(crate::error::FireboltError::Serialization(format!(
193                "Cannot convert {column_type:?} to Option<f32>"
194            ))),
195        }
196    }
197}
198
199impl TypeConversion for f64 {
200    fn convert_from_json(
201        value: &serde_json::Value,
202        column_type: &Type,
203    ) -> Result<Self, crate::error::FireboltError> {
204        match column_type {
205            Type::Double => {
206                if value.is_null() {
207                    return Err(crate::error::FireboltError::Serialization(
208                        "Cannot convert null to non-nullable type".to_string(),
209                    ));
210                }
211                value.as_f64().ok_or_else(|| {
212                    crate::error::FireboltError::Serialization(
213                        "Failed to convert to f64".to_string(),
214                    )
215                })
216            }
217            _ => Err(crate::error::FireboltError::Serialization(format!(
218                "Cannot convert {column_type:?} to f64"
219            ))),
220        }
221    }
222}
223
224impl TypeConversion for Option<f64> {
225    fn convert_from_json(
226        value: &serde_json::Value,
227        column_type: &Type,
228    ) -> Result<Self, crate::error::FireboltError> {
229        if value.is_null() {
230            return Ok(None);
231        }
232        match column_type {
233            Type::Double => {
234                let val = value.as_f64().ok_or_else(|| {
235                    crate::error::FireboltError::Serialization(
236                        "Failed to convert to f64".to_string(),
237                    )
238                })?;
239                Ok(Some(val))
240            }
241            _ => Err(crate::error::FireboltError::Serialization(format!(
242                "Cannot convert {column_type:?} to Option<f64>"
243            ))),
244        }
245    }
246}
247
248impl TypeConversion for rust_decimal::Decimal {
249    fn convert_from_json(
250        value: &serde_json::Value,
251        column_type: &Type,
252    ) -> Result<Self, crate::error::FireboltError> {
253        match column_type {
254            Type::Decimal => {
255                if value.is_null() {
256                    return Err(crate::error::FireboltError::Serialization(
257                        "Cannot convert null to non-nullable type".to_string(),
258                    ));
259                }
260                if let Some(s) = value.as_str() {
261                    s.parse().map_err(|_| {
262                        crate::error::FireboltError::Serialization(
263                            "Failed to parse Decimal from string".to_string(),
264                        )
265                    })
266                } else if let Some(f) = value.as_f64() {
267                    rust_decimal::Decimal::try_from(f).map_err(|_| {
268                        crate::error::FireboltError::Serialization(
269                            "Failed to convert f64 to Decimal".to_string(),
270                        )
271                    })
272                } else {
273                    Err(crate::error::FireboltError::Serialization(
274                        "Failed to convert to Decimal".to_string(),
275                    ))
276                }
277            }
278            _ => Err(crate::error::FireboltError::Serialization(format!(
279                "Cannot convert {column_type:?} to Decimal"
280            ))),
281        }
282    }
283}
284
285impl TypeConversion for Option<rust_decimal::Decimal> {
286    fn convert_from_json(
287        value: &serde_json::Value,
288        column_type: &Type,
289    ) -> Result<Self, crate::error::FireboltError> {
290        if value.is_null() {
291            return Ok(None);
292        }
293        match column_type {
294            Type::Decimal => {
295                let val = if let Some(s) = value.as_str() {
296                    s.parse().map_err(|_| {
297                        crate::error::FireboltError::Serialization(
298                            "Failed to parse Decimal from string".to_string(),
299                        )
300                    })?
301                } else if let Some(f) = value.as_f64() {
302                    rust_decimal::Decimal::try_from(f).map_err(|_| {
303                        crate::error::FireboltError::Serialization(
304                            "Failed to convert f64 to Decimal".to_string(),
305                        )
306                    })?
307                } else {
308                    return Err(crate::error::FireboltError::Serialization(
309                        "Failed to convert to Decimal".to_string(),
310                    ));
311                };
312                Ok(Some(val))
313            }
314            _ => Err(crate::error::FireboltError::Serialization(format!(
315                "Cannot convert {column_type:?} to Option<Decimal>"
316            ))),
317        }
318    }
319}
320
321impl TypeConversion for String {
322    fn convert_from_json(
323        value: &serde_json::Value,
324        column_type: &Type,
325    ) -> Result<Self, crate::error::FireboltError> {
326        match column_type {
327            Type::Text => {
328                if value.is_null() {
329                    return Err(crate::error::FireboltError::Serialization(
330                        "Cannot convert null to non-nullable type".to_string(),
331                    ));
332                }
333                value.as_str().map(|s| s.to_string()).ok_or_else(|| {
334                    crate::error::FireboltError::Serialization(
335                        "Failed to convert to String".to_string(),
336                    )
337                })
338            }
339            _ => Err(crate::error::FireboltError::Serialization(format!(
340                "Cannot convert {column_type:?} to String"
341            ))),
342        }
343    }
344}
345
346impl TypeConversion for Option<String> {
347    fn convert_from_json(
348        value: &serde_json::Value,
349        column_type: &Type,
350    ) -> Result<Self, crate::error::FireboltError> {
351        if value.is_null() {
352            return Ok(None);
353        }
354        match column_type {
355            Type::Text => {
356                let val = value.as_str().map(|s| s.to_string()).ok_or_else(|| {
357                    crate::error::FireboltError::Serialization(
358                        "Failed to convert to String".to_string(),
359                    )
360                })?;
361                Ok(Some(val))
362            }
363            _ => Err(crate::error::FireboltError::Serialization(format!(
364                "Cannot convert {column_type:?} to Option<String>"
365            ))),
366        }
367    }
368}
369
370impl TypeConversion for bool {
371    fn convert_from_json(
372        value: &serde_json::Value,
373        column_type: &Type,
374    ) -> Result<Self, crate::error::FireboltError> {
375        match column_type {
376            Type::Boolean => {
377                if value.is_null() {
378                    return Err(crate::error::FireboltError::Serialization(
379                        "Cannot convert null to non-nullable type".to_string(),
380                    ));
381                }
382                value.as_bool().ok_or_else(|| {
383                    crate::error::FireboltError::Serialization(
384                        "Failed to convert to bool".to_string(),
385                    )
386                })
387            }
388            _ => Err(crate::error::FireboltError::Serialization(format!(
389                "Cannot convert {column_type:?} to bool"
390            ))),
391        }
392    }
393}
394
395impl TypeConversion for Option<bool> {
396    fn convert_from_json(
397        value: &serde_json::Value,
398        column_type: &Type,
399    ) -> Result<Self, crate::error::FireboltError> {
400        if value.is_null() {
401            return Ok(None);
402        }
403        match column_type {
404            Type::Boolean => {
405                let val = value.as_bool().ok_or_else(|| {
406                    crate::error::FireboltError::Serialization(
407                        "Failed to convert to bool".to_string(),
408                    )
409                })?;
410                Ok(Some(val))
411            }
412            _ => Err(crate::error::FireboltError::Serialization(format!(
413                "Cannot convert {column_type:?} to Option<bool>"
414            ))),
415        }
416    }
417}
418
419impl TypeConversion for Vec<u8> {
420    fn convert_from_json(
421        value: &serde_json::Value,
422        column_type: &Type,
423    ) -> Result<Self, crate::error::FireboltError> {
424        match column_type {
425            Type::Bytes => {
426                if value.is_null() {
427                    return Err(crate::error::FireboltError::Serialization(
428                        "Cannot convert null to non-nullable type".to_string(),
429                    ));
430                }
431                if let Some(s) = value.as_str() {
432                    if let Some(stripped) = s.strip_prefix("\\x") {
433                        hex::decode(stripped).map_err(|_| {
434                            crate::error::FireboltError::Serialization(
435                                "Failed to decode hex string".to_string(),
436                            )
437                        })
438                    } else {
439                        Ok(s.as_bytes().to_vec())
440                    }
441                } else {
442                    Err(crate::error::FireboltError::Serialization(
443                        "Failed to convert to Vec<u8>".to_string(),
444                    ))
445                }
446            }
447            _ => Err(crate::error::FireboltError::Serialization(format!(
448                "Cannot convert {column_type:?} to Vec<u8>"
449            ))),
450        }
451    }
452}
453
454impl TypeConversion for Option<Vec<u8>> {
455    fn convert_from_json(
456        value: &serde_json::Value,
457        column_type: &Type,
458    ) -> Result<Self, crate::error::FireboltError> {
459        if value.is_null() {
460            return Ok(None);
461        }
462        match column_type {
463            Type::Bytes => {
464                let val = if let Some(s) = value.as_str() {
465                    if let Some(stripped) = s.strip_prefix("\\x") {
466                        hex::decode(stripped).map_err(|_| {
467                            crate::error::FireboltError::Serialization(
468                                "Failed to decode hex string".to_string(),
469                            )
470                        })?
471                    } else {
472                        s.as_bytes().to_vec()
473                    }
474                } else {
475                    return Err(crate::error::FireboltError::Serialization(
476                        "Failed to convert to Vec<u8>".to_string(),
477                    ));
478                };
479                Ok(Some(val))
480            }
481            _ => Err(crate::error::FireboltError::Serialization(format!(
482                "Cannot convert {column_type:?} to Option<Vec<u8>>"
483            ))),
484        }
485    }
486}
487
488impl TypeConversion for serde_json::Value {
489    fn convert_from_json(
490        value: &serde_json::Value,
491        _column_type: &Type,
492    ) -> Result<Self, crate::error::FireboltError> {
493        Ok(value.clone())
494    }
495}
496
497#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
498pub struct Column {
499    pub name: String,
500    pub r#type: Type,
501    pub precision: Option<i32>,
502    pub scale: Option<i32>,
503    pub is_nullable: bool,
504}
505
506#[derive(Debug, Clone)]
507pub enum ColumnRef {
508    Index(usize),
509    Name(String),
510}
511
512impl From<usize> for ColumnRef {
513    fn from(index: usize) -> Self {
514        ColumnRef::Index(index)
515    }
516}
517
518impl From<String> for ColumnRef {
519    fn from(name: String) -> Self {
520        ColumnRef::Name(name)
521    }
522}
523
524impl From<&str> for ColumnRef {
525    fn from(name: &str) -> Self {
526        ColumnRef::Name(name.to_string())
527    }
528}