derive_sql/traits/
row.rs

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::DATE.name()) => true,
26      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::DATE.name()) => true,
43      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
273/// Return a row of results that can be queried to be converted into objects
274pub 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
285/// Trait to be implemented to allow a `Row` to be converted into an object
286pub 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}