1use super::{Value, ValueRef};
2use std::convert::TryInto;
3use std::error::Error;
4use std::fmt;
5
6#[derive(Debug)]
8#[non_exhaustive]
9pub enum FromSqlError {
10    InvalidType,
13
14    OutOfRange(i64),
17
18    InvalidBlobSize {
21        expected_size: usize,
23        blob_size: usize,
25    },
26
27    Other(Box<dyn Error + Send + Sync + 'static>),
29}
30
31impl PartialEq for FromSqlError {
32    fn eq(&self, other: &FromSqlError) -> bool {
33        match (self, other) {
34            (FromSqlError::InvalidType, FromSqlError::InvalidType) => true,
35            (FromSqlError::OutOfRange(n1), FromSqlError::OutOfRange(n2)) => n1 == n2,
36            (
37                FromSqlError::InvalidBlobSize {
38                    expected_size: es1,
39                    blob_size: bs1,
40                },
41                FromSqlError::InvalidBlobSize {
42                    expected_size: es2,
43                    blob_size: bs2,
44                },
45            ) => es1 == es2 && bs1 == bs2,
46            (..) => false,
47        }
48    }
49}
50
51impl fmt::Display for FromSqlError {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        match *self {
54            FromSqlError::InvalidType => write!(f, "Invalid type"),
55            FromSqlError::OutOfRange(i) => write!(f, "Value {i} out of range"),
56            FromSqlError::InvalidBlobSize {
57                expected_size,
58                blob_size,
59            } => {
60                write!(
61                    f,
62                    "Cannot read {} byte value out of {} byte blob",
63                    expected_size, blob_size
64                )
65            }
66            FromSqlError::Other(ref err) => err.fmt(f),
67        }
68    }
69}
70
71impl Error for FromSqlError {
72    fn source(&self) -> Option<&(dyn Error + 'static)> {
73        if let FromSqlError::Other(ref err) = self {
74            Some(&**err)
75        } else {
76            None
77        }
78    }
79}
80
81pub type FromSqlResult<T> = Result<T, FromSqlError>;
83
84pub trait FromSql: Sized {
86    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self>;
88}
89
90macro_rules! from_sql_integral(
91    ($t:ident) => (
92        impl FromSql for $t {
93            #[inline]
94            fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
95                let i = i64::column_result(value)?;
96                i.try_into().map_err(|_| FromSqlError::OutOfRange(i))
97            }
98        }
99    )
100);
101
102from_sql_integral!(i8);
103from_sql_integral!(i16);
104from_sql_integral!(i32);
105from_sql_integral!(isize);
107from_sql_integral!(u8);
108from_sql_integral!(u16);
109from_sql_integral!(u32);
110from_sql_integral!(u64);
111from_sql_integral!(usize);
112
113impl FromSql for i64 {
114    #[inline]
115    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
116        value.as_i64()
117    }
118}
119
120impl FromSql for f32 {
121    #[inline]
122    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
123        match value {
124            ValueRef::Integer(i) => Ok(i as f32),
125            ValueRef::Real(f) => Ok(f as f32),
126            _ => Err(FromSqlError::InvalidType),
127        }
128    }
129}
130
131impl FromSql for f64 {
132    #[inline]
133    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
134        match value {
135            ValueRef::Integer(i) => Ok(i as f64),
136            ValueRef::Real(f) => Ok(f),
137            _ => Err(FromSqlError::InvalidType),
138        }
139    }
140}
141
142impl FromSql for bool {
143    #[inline]
144    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
145        i64::column_result(value).map(|i| i != 0)
146    }
147}
148
149impl FromSql for String {
150    #[inline]
151    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
152        value.as_str().map(ToString::to_string)
153    }
154}
155
156impl FromSql for Box<str> {
157    #[inline]
158    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
159        value.as_str().map(Into::into)
160    }
161}
162
163impl FromSql for std::rc::Rc<str> {
164    #[inline]
165    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
166        value.as_str().map(Into::into)
167    }
168}
169
170impl FromSql for std::sync::Arc<str> {
171    #[inline]
172    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
173        value.as_str().map(Into::into)
174    }
175}
176
177impl FromSql for Vec<u8> {
178    #[inline]
179    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
180        value.as_blob().map(<[u8]>::to_vec)
181    }
182}
183
184impl<const N: usize> FromSql for [u8; N] {
185    #[inline]
186    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
187        let slice = value.as_blob()?;
188        slice.try_into().map_err(|_| FromSqlError::InvalidBlobSize {
189            expected_size: N,
190            blob_size: slice.len(),
191        })
192    }
193}
194
195#[cfg(feature = "i128_blob")]
196#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
197impl FromSql for i128 {
198    #[inline]
199    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
200        let bytes = <[u8; 16]>::column_result(value)?;
201        Ok(i128::from_be_bytes(bytes) ^ (1_i128 << 127))
202    }
203}
204
205#[cfg(feature = "uuid")]
206#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
207impl FromSql for uuid::Uuid {
208    #[inline]
209    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
210        let bytes = <[u8; 16]>::column_result(value)?;
211        Ok(uuid::Uuid::from_u128(u128::from_be_bytes(bytes)))
212    }
213}
214
215impl<T: FromSql> FromSql for Option<T> {
216    #[inline]
217    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
218        match value {
219            ValueRef::Null => Ok(None),
220            _ => FromSql::column_result(value).map(Some),
221        }
222    }
223}
224
225impl FromSql for Value {
226    #[inline]
227    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
228        Ok(value.into())
229    }
230}
231
232#[cfg(test)]
233mod test {
234    use super::FromSql;
235    use crate::{Connection, Error, Result};
236
237    #[test]
238    fn test_integral_ranges() -> Result<()> {
239        let db = Connection::open_in_memory()?;
240
241        fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64])
242        where
243            T: Into<i64> + FromSql + std::fmt::Debug,
244        {
245            for n in out_of_range {
246                let err = db
247                    .query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
248                    .unwrap_err();
249                match err {
250                    Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
251                    _ => panic!("unexpected error: {}", err),
252                }
253            }
254            for n in in_range {
255                assert_eq!(
256                    *n,
257                    db.query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
258                        .unwrap()
259                        .into()
260                );
261            }
262        }
263
264        check_ranges::<i8>(&db, &[-129, 128], &[-128, 0, 1, 127]);
265        check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]);
266        check_ranges::<i32>(
267            &db,
268            &[-2_147_483_649, 2_147_483_648],
269            &[-2_147_483_648, -1, 0, 1, 2_147_483_647],
270        );
271        check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]);
272        check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]);
273        check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]);
274        Ok(())
275    }
276}