1use super::*;
2
3#[derive(Debug, Clone)]
4pub enum Value {
5 Null,
6 Bool(bool),
7 Integer(i64),
8 UInteger(u64),
9 Real(f64),
10 Text(String),
11 Blob(Vec<u8>),
12#[cfg(feature = "mysql")]
13 MysqlValue(::mysql::Value),
14}
15
16
17#[cfg(feature = "postgres")]
18impl<'a> postgres::types::FromSql<'a> for Value {
19 fn from_sql(ty: &postgres::types::Type, raw: &'a [u8]) -> std::result::Result<Self, Box<dyn core::error::Error + core::marker::Sync + core::marker::Send>> {
20 use postgres::types::Type;
21 let name = ty.name();
22 match true {
23 true if raw.len() == 0 => Ok(Value::Null),
24 true if name.eq(Type::BOOL.name()) => Ok(Value::Bool(<bool as postgres::types::FromSql>::from_sql(ty, raw)?)),
25 true if name.eq(Type::FLOAT4.name()) => Ok(Value::Real(<f32 as postgres::types::FromSql>::from_sql(ty, raw)?.into())),
27 true if name.eq(Type::FLOAT8.name()) => Ok(Value::Real(<f64 as postgres::types::FromSql>::from_sql(ty, raw)?.into())),
28 true if name.eq(Type::INT2.name()) => Ok(Value::Integer(<i16 as postgres::types::FromSql>::from_sql(ty, raw)?.into())),
29 true if name.eq(Type::INT4.name()) => Ok(Value::Integer(<i32 as postgres::types::FromSql>::from_sql(ty, raw)?.into())),
30 true if name.eq(Type::INT8.name()) => Ok(Value::Integer(<i64 as postgres::types::FromSql>::from_sql(ty, raw)?.into())),
31 true if name.eq(Type::TEXT.name()) => Ok(Value::Text(<String as postgres::types::FromSql>::from_sql(ty, raw)?.into())),
32 true if name.eq(Type::BYTEA.name()) => Ok(Value::Blob(<Vec<u8> as postgres::types::FromSql>::from_sql(ty, raw)?.into())),
33 _ => Err(Error::PostgreSQLInvalidConversion(name.to_string()).into()),
34 }
35 }
36
37 fn accepts(ty: &postgres::types::Type) -> bool {
38 use postgres::types::Type;
39 let name = ty.name();
40 match true {
41 true if name.eq(Type::BOOL.name()) => true,
42 true if name.eq(Type::FLOAT4.name()) => true,
44 true if name.eq(Type::FLOAT8.name()) => true,
45 true if name.eq(Type::INT4.name()) => true,
46 true if name.eq(Type::INT8.name()) => true,
47 true if name.eq(Type::TEXT.name()) => true,
48 true if name.eq(Type::BYTEA.name()) => true,
49 _ => false,
50 }
51 }
52}
53
54#[cfg(feature = "mysql")]
55impl std::convert::TryFrom<::mysql::Value> for Value {
56 type Error = Error;
57 fn try_from(v: ::mysql::Value) -> Result<Self> {
58 Ok(Value::MysqlValue(v))
59 }
60}
61
62pub trait TryFromValue {
63 fn try_from(v: Value) -> Result<Self> where Self: Sized;
64}
65
66impl TryFromValue for Vec<u8> {
67 fn try_from(v: Value) -> Result<Self> {
68 match v {
69 Value::Blob(v) => Ok(v),
70#[cfg(feature = "mysql")]
71 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
72 _ => Err(Error::InvalidTypeFor("Vec<u8>".to_string())),
73 }
74 }
75}
76
77impl TryFromValue for bool {
78 fn try_from(v: Value) -> Result<Self> {
79 match v {
80 Value::Bool(v) => Ok(v),
81 Value::Integer(v) => Ok(v != 0),
82#[cfg(feature = "mysql")]
83 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
84 _ => Err(Error::InvalidTypeFor("bool".to_string())),
85 }
86 }
87}
88
89impl TryFromValue for usize {
90 fn try_from(v: Value) -> Result<Self> {
91 match v {
92 Value::UInteger(v) => Ok(v.try_into()?),
93 Value::Integer(v) => Ok(v.try_into()?),
94#[cfg(feature = "mysql")]
95 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
96 _ => Err(Error::InvalidTypeForFrom("usize".to_string(), format!("{v:?}"))),
97 }
98 }
99}
100
101impl TryFromValue for u8 {
102 fn try_from(v: Value) -> Result<Self> {
103 match v {
104 Value::UInteger(v) => Ok(v.try_into()?),
105 Value::Integer(v) => Ok(v.try_into()?),
106#[cfg(feature = "mysql")]
107 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
108 _ => Err(Error::InvalidTypeForFrom("u8".to_string(), format!("{v:?}"))),
109 }
110 }
111}
112
113impl TryFromValue for u16 {
114 fn try_from(v: Value) -> Result<Self> {
115 match v {
116 Value::UInteger(v) => Ok(v.try_into()?),
117 Value::Integer(v) => Ok(v.try_into()?),
118#[cfg(feature = "mysql")]
119 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
120 _ => Err(Error::InvalidTypeForFrom("u16".to_string(), format!("{v:?}"))),
121 }
122 }
123}
124
125impl TryFromValue for u32 {
126 fn try_from(v: Value) -> Result<Self> {
127 match v {
128 Value::UInteger(v) => Ok(v.try_into()?),
129 Value::Integer(v) => Ok(v.try_into()?),
130#[cfg(feature = "mysql")]
131 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
132 _ => Err(Error::InvalidTypeForFrom("u32".to_string(), format!("{v:?}"))),
133 }
134 }
135}
136
137impl TryFromValue for u64 {
138 fn try_from(v: Value) -> Result<Self> {
139 match v {
140 Value::UInteger(v) => Ok(v),
141 Value::Integer(v) => Ok(v.try_into()?),
142#[cfg(feature = "mysql")]
143 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
144 _ => Err(Error::InvalidTypeForFrom("u64".to_string(), format!("{v:?}"))),
145 }
146 }
147}
148
149impl TryFromValue for isize {
150 fn try_from(v: Value) -> Result<Self> {
151 match v {
152 Value::Integer(v) => Ok(v.try_into()?),
153#[cfg(feature = "mysql")]
154 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
155 _ => Err(Error::InvalidTypeFor("isize".to_string())),
156 }
157 }
158}
159
160impl TryFromValue for i8 {
161 fn try_from(v: Value) -> Result<Self> {
162 match v {
163 Value::Integer(v) => Ok(v.try_into()?),
164#[cfg(feature = "mysql")]
165 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
166 _ => Err(Error::InvalidTypeFor("i8".to_string())),
167 }
168 }
169}
170
171impl TryFromValue for i16 {
172 fn try_from(v: Value) -> Result<Self> {
173 match v {
174 Value::Integer(v) => Ok(v.try_into()?),
175#[cfg(feature = "mysql")]
176 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
177 _ => Err(Error::InvalidTypeFor("i16".to_string())),
178 }
179 }
180}
181
182impl TryFromValue for i32 {
183 fn try_from(v: Value) -> Result<Self> {
184 match v {
185 Value::Integer(v) => Ok(v.try_into()?),
186#[cfg(feature = "mysql")]
187 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
188 _ => Err(Error::InvalidTypeFor("i32".to_string())),
189 }
190 }
191}
192
193impl TryFromValue for i64 {
194 fn try_from(v: Value) -> Result<Self> {
195 match v {
196 Value::Integer(v) => Ok(v),
197#[cfg(feature = "mysql")]
198 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
199 _ => Err(Error::InvalidTypeFor("i64".to_string())),
200 }
201 }
202}
203
204impl TryFromValue for f32 {
205 fn try_from(v: Value) -> Result<Self> {
206 match v {
207 Value::Real(v) => Ok(v as f32),
208#[cfg(feature = "mysql")]
209 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
210 _ => Err(Error::InvalidTypeFor("f32".to_string())),
211 }
212 }
213}
214
215impl TryFromValue for f64 {
216 fn try_from(v: Value) -> Result<Self> {
217 match v {
218 Value::Real(v) => Ok(v),
219#[cfg(feature = "mysql")]
220 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
221 _ => Err(Error::InvalidTypeFor("f64".to_string())),
222 }
223 }
224}
225
226impl TryFromValue for String {
227 fn try_from(v: Value) -> Result<Self> {
228 match v {
229 Value::Text(v) => Ok(v),
230 Value::Blob(v) => Ok(String::from_utf8(v)?),
231#[cfg(feature = "mysql")]
232 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
233 _ => Err(Error::InvalidTypeForFrom("String".to_string(), format!("{v:?}"))),
234 }
235 }
236}
237
238impl TryFromValue for chrono::naive::NaiveDate {
239 fn try_from(v: Value) -> Result<Self> {
240 match v {
241 Value::Text(v) => Ok(chrono::naive::NaiveDate::parse_from_str(v.as_str(), "%Y-%m-%d")?),
242#[cfg(feature = "mysql")]
243 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
244 _ => Err(Error::InvalidTypeForFrom("NaiveDate".to_string(), format!("{v:?}"))),
245 }
246 }
247}
248
249impl TryFromValue for chrono::naive::NaiveDateTime {
250 fn try_from(v: Value) -> Result<Self> {
251 match v {
252#[cfg(feature = "mysql")]
253 Value::MysqlValue(v) => Ok(::mysql::from_value_opt(v)?),
254 _ => Err(Error::InvalidTypeForFrom("NaiveDateTime".to_string(), format!("{v:?}"))),
255 }
256 }
257}
258
259
260impl<T> TryFromValue for Option<T>
261where T: TryFromValue,
262{
263 fn try_from(v: Value) -> Result<Self> {
264 match v {
265 Value::Null => Ok(None),
266#[cfg(feature = "mysql")]
267 Value::MysqlValue(::mysql::Value::NULL) => Ok(None),
268 _ => Ok(Some(TryFromValue::try_from(v)?)),
269 }
270 }
271}
272
273pub trait Row {
275 fn get_value(&self, i: usize) -> Option<Result<Value>>;
276
277 fn get<T>(&self, i: usize) -> Option<Result<T>>
278 where T: TryFromValue,
279 {
280 self.get_value(i)
281 .map(|v| v.and_then(|r| TryFromValue::try_from(r) ) )
282 }
283}
284
285pub trait TryFromRefRow<R>
287where R: Row,
288{
289 fn try_from(r: &R) -> Result<Self> where Self: Sized;
290}
291
292impl<R, T> TryFromRefRow<R> for T
293where R: Row,
294 T: TryFromValue + Sized,
295{
296 fn try_from(r: &R) -> Result<Self> {
297 Ok(r.get(0).ok_or(Error::RowItemNotFound(0))??)
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[test]
306 fn it_retrieves_a_string() -> Result<()> {
307 struct MyRow {}
308 impl Row for MyRow { fn get_value(&self, _i: usize) -> Option<Result<Value>> { Some(Ok(Value::Text(format!("hello")))) } }
309 let r: String = <String as TryFromRefRow<_>>::try_from(&MyRow {})?;
310 assert!(r.eq("hello"));
311
312 struct MyRow2 {}
313 impl Row for MyRow2 { fn get_value(&self, _i: usize) -> Option<Result<Value>> { None } }
314 assert!(<String as TryFromRefRow<_>>::try_from(&MyRow2 {}).is_err());
315
316 Ok(())
317 }
318}