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