1use crate::{
4 error::{err_column_null, err_type_conv},
5 FbError, SqlType,
6};
7
8pub use SqlType::*;
9
10pub struct Row {
12 pub cols: Vec<Column>,
13}
14
15impl Row {
16 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 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)]
55pub 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
148impl<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
162pub trait FromRow {
164 fn try_from(row: Vec<Column>) -> Result<Self, FbError>
165 where
166 Self: std::marker::Sized;
167}
168
169impl 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
180impl FromRow for () {
182 fn try_from(_row: Vec<Column>) -> Result<Self, FbError>
183 where
184 Self: Sized,
185 {
186 Ok(())
187 }
188}
189
190macro_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
217macro_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);