rsfbclient_core/
row.rs

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