Skip to main content

rsfbclient_core/
row.rs

1//! Sql column types and traits
2
3use std::sync::Arc;
4
5use crate::{
6    error::{err_column_null, err_type_conv},
7    FbError, SqlType,
8};
9
10pub use SqlType::*;
11
12/// A database row
13pub struct Row {
14    pub cols: Vec<Column>,
15}
16
17impl Row {
18    /// Get the column value by the index
19    pub fn get<T>(&self, idx: usize) -> Result<T, FbError>
20    where
21        Column: ColumnToVal<T>,
22    {
23        if let Some(col) = self.cols.get(idx) {
24            col.clone().to_val()
25        } else {
26            Err("This index doesn't exists".into())
27        }
28    }
29
30    /// Get the values for all columns
31    pub fn get_all<T>(self) -> Result<T, FbError>
32    where
33        T: FromRow,
34    {
35        <T as FromRow>::try_from(self.cols)
36    }
37}
38
39#[derive(Debug, Clone)]
40pub struct Column {
41    pub value: SqlType,
42    pub raw_type: u32,
43    /// Column name/alias. `Arc<str>` so the driver can clone it into every row
44    /// with just a refcount bump — the name is constant across a result set, and
45    /// cloning a `String` per column per row was the dominant decode allocation.
46    pub name: Arc<str>,
47}
48
49impl Column {
50    pub fn new(name: impl Into<Arc<str>>, raw_type: u32, value: SqlType) -> Self {
51        Column {
52            name: name.into(),
53            raw_type,
54            value,
55        }
56    }
57}
58
59#[allow(clippy::wrong_self_convention)]
60/// Define the conversion from the buffer to a value
61pub trait ColumnToVal<T> {
62    fn to_val(self) -> Result<T, FbError>
63    where
64        Self: std::marker::Sized;
65}
66
67impl ColumnToVal<String> for Column {
68    fn to_val(self) -> Result<String, FbError> {
69        match self.value {
70            Text(t) => Ok(t),
71
72            Integer(i) => Ok(i.to_string()),
73
74            Int128(i) => Ok(i.to_string()),
75
76            Floating(f) => Ok(f.to_string()),
77
78            Timestamp(ts) => Ok(ts.to_string()),
79
80            Binary(_) => Err("This is a binary column. You cannot use string to access".into()),
81
82            Boolean(bo) => Ok(bo.to_string()),
83
84            Null => Err(err_column_null("String")),
85        }
86    }
87}
88
89impl ColumnToVal<i128> for Column {
90    fn to_val(self) -> Result<i128, FbError> {
91        match self.value {
92            Integer(i) => Ok(i128::from(i)),
93
94            Int128(i) => Ok(i),
95
96            Null => Err(err_column_null("i128")),
97
98            col => err_type_conv(col, "i128"),
99        }
100    }
101}
102
103impl ColumnToVal<i64> for Column {
104    fn to_val(self) -> Result<i64, FbError> {
105        match self.value {
106            Integer(i) => Ok(i),
107
108            Null => Err(err_column_null("i64")),
109
110            col => err_type_conv(col, "i64"),
111        }
112    }
113}
114
115impl ColumnToVal<i32> for Column {
116    fn to_val(self) -> Result<i32, FbError> {
117        ColumnToVal::<i64>::to_val(self).map(|i| i as i32)
118    }
119}
120
121impl ColumnToVal<i16> for Column {
122    fn to_val(self) -> Result<i16, FbError> {
123        ColumnToVal::<i64>::to_val(self).map(|i| i as i16)
124    }
125}
126
127impl ColumnToVal<f64> for Column {
128    fn to_val(self) -> Result<f64, FbError> {
129        match self.value {
130            Floating(f) => Ok(f),
131
132            Null => Err(err_column_null("f64")),
133
134            col => err_type_conv(col, "f64"),
135        }
136    }
137}
138
139impl ColumnToVal<f32> for Column {
140    fn to_val(self) -> Result<f32, FbError> {
141        ColumnToVal::<f64>::to_val(self).map(|i| i as f32)
142    }
143}
144
145impl ColumnToVal<Vec<u8>> for Column {
146    fn to_val(self) -> Result<Vec<u8>, FbError> {
147        match self.value {
148            Binary(b) => Ok(b),
149
150            Null => Err(err_column_null("Vec<u8>")),
151
152            col => err_type_conv(col, "Vec<u8>"),
153        }
154    }
155}
156
157impl ColumnToVal<bool> for Column {
158    fn to_val(self) -> Result<bool, FbError> {
159        match self.value {
160            Boolean(bo) => Ok(bo),
161
162            Null => Err(err_column_null("bool")),
163
164            col => err_type_conv(col, "bool"),
165        }
166    }
167}
168
169/// Implements for all nullable variants
170impl<T> ColumnToVal<Option<T>> for Column
171where
172    Column: ColumnToVal<T>,
173{
174    fn to_val(self) -> Result<Option<T>, FbError> {
175        if self.value.is_null() {
176            return Ok(None);
177        }
178
179        Ok(Some(self.to_val()?))
180    }
181}
182
183/// Implemented for types that represents a list of values of columns
184pub trait FromRow {
185    fn try_from(row: Vec<Column>) -> Result<Self, FbError>
186    where
187        Self: std::marker::Sized;
188}
189
190/// Allow use of a vector instead of tuples, for when the number of columns are unknow at compile time
191/// or more columns are needed than what can be used with the tuples
192impl FromRow for Row {
193    fn try_from(row: Vec<Column>) -> Result<Self, FbError>
194    where
195        Self: Sized,
196    {
197        Ok(Row { cols: row })
198    }
199}
200
201/// For no columns
202impl FromRow for () {
203    fn try_from(_row: Vec<Column>) -> Result<Self, FbError>
204    where
205        Self: Sized,
206    {
207        Ok(())
208    }
209}
210
211/// Generates FromRow implementations for a tuple
212macro_rules! impl_from_row {
213    ($($t: ident),+) => {
214        impl<'a, $($t),+> FromRow for ($($t,)+)
215        where
216            $( Column: ColumnToVal<$t>, )+
217        {
218            fn try_from(row: Vec<Column>) -> Result<Self, FbError> {
219                let len = row.len();
220                let mut iter = row.into_iter();
221
222                Ok(( $(
223                    ColumnToVal::<$t>::to_val(
224                        iter
225                            .next()
226                            .ok_or_else(|| {
227                                FbError::Other(
228                                    format!("The sql returned less columns than the {} expected", len),
229                                )
230                            })?
231                    )?,
232                )+ ))
233            }
234        }
235    };
236}
237
238/// Generates FromRow implementations for various tuples
239macro_rules! impls_from_row {
240    ($t: ident) => {
241        impl_from_row!($t);
242    };
243
244    ($t: ident, $($ts: ident),+ ) => {
245        impls_from_row!($($ts),+);
246
247        impl_from_row!($t, $($ts),+);
248    };
249}
250
251impls_from_row!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);