diff options
Diffstat (limited to 'src/types/from_sql.rs')
-rw-r--r-- | src/types/from_sql.rs | 79 |
1 files changed, 39 insertions, 40 deletions
diff --git a/src/types/from_sql.rs b/src/types/from_sql.rs index 3fe74b4..7381fdf 100644 --- a/src/types/from_sql.rs +++ b/src/types/from_sql.rs @@ -1,8 +1,9 @@ use super::{Value, ValueRef}; +use std::convert::TryInto; use std::error::Error; use std::fmt; -/// Enum listing possible errors from `FromSql` trait. +/// Enum listing possible errors from [`FromSql`] trait. #[derive(Debug)] #[non_exhaustive] pub enum FromSqlError { @@ -25,7 +26,7 @@ pub enum FromSqlError { #[cfg(feature = "uuid")] InvalidUuidSize(usize), - /// An error case available for implementors of the `FromSql` trait. + /// An error case available for implementors of the [`FromSql`] trait. Other(Box<dyn Error + Send + Sync + 'static>), } @@ -71,48 +72,22 @@ impl Error for FromSqlError { } } -/// Result type for implementors of the `FromSql` trait. +/// Result type for implementors of the [`FromSql`] trait. pub type FromSqlResult<T> = Result<T, FromSqlError>; /// A trait for types that can be created from a SQLite value. -/// -/// Note that `FromSql` and `ToSql` are defined for most integral types, but -/// not `u64` or `usize`. This is intentional; SQLite returns integers as -/// signed 64-bit values, which cannot fully represent the range of these -/// types. Rusqlite would have to -/// decide how to handle negative values: return an error or reinterpret as a -/// very large postive numbers, neither of which -/// is guaranteed to be correct for everyone. Callers can work around this by -/// fetching values as i64 and then doing the interpretation themselves or by -/// defining a newtype and implementing `FromSql`/`ToSql` for it. pub trait FromSql: Sized { /// Converts SQLite value into Rust value. fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self>; } -impl FromSql for isize { - fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { - i64::column_result(value).and_then(|i| { - if i < isize::min_value() as i64 || i > isize::max_value() as i64 { - Err(FromSqlError::OutOfRange(i)) - } else { - Ok(i as isize) - } - }) - } -} - macro_rules! from_sql_integral( ($t:ident) => ( impl FromSql for $t { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { - i64::column_result(value).and_then(|i| { - if i < i64::from($t::min_value()) || i > i64::from($t::max_value()) { - Err(FromSqlError::OutOfRange(i)) - } else { - Ok(i as $t) - } - }) + let i = i64::column_result(value)?; + i.try_into().map_err(|_| FromSqlError::OutOfRange(i)) } } ) @@ -121,17 +96,34 @@ macro_rules! from_sql_integral( from_sql_integral!(i8); from_sql_integral!(i16); from_sql_integral!(i32); +// from_sql_integral!(i64); // Not needed because the native type is i64. +from_sql_integral!(isize); from_sql_integral!(u8); from_sql_integral!(u16); from_sql_integral!(u32); +from_sql_integral!(u64); +from_sql_integral!(usize); impl FromSql for i64 { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { value.as_i64() } } +impl FromSql for f32 { + #[inline] + fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { + match value { + ValueRef::Integer(i) => Ok(i as f32), + ValueRef::Real(f) => Ok(f as f32), + _ => Err(FromSqlError::InvalidType), + } + } +} + impl FromSql for f64 { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { match value { ValueRef::Integer(i) => Ok(i as f64), @@ -142,36 +134,42 @@ impl FromSql for f64 { } impl FromSql for bool { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { - i64::column_result(value).map(|i| !matches!(i, 0)) + i64::column_result(value).map(|i| i != 0) } } impl FromSql for String { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { value.as_str().map(ToString::to_string) } } impl FromSql for Box<str> { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { value.as_str().map(Into::into) } } impl FromSql for std::rc::Rc<str> { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { value.as_str().map(Into::into) } } impl FromSql for std::sync::Arc<str> { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { value.as_str().map(Into::into) } } impl FromSql for Vec<u8> { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { value.as_blob().map(|b| b.to_vec()) } @@ -179,6 +177,7 @@ impl FromSql for Vec<u8> { #[cfg(feature = "i128_blob")] impl FromSql for i128 { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { use byteorder::{BigEndian, ByteOrder}; @@ -194,6 +193,7 @@ impl FromSql for i128 { #[cfg(feature = "uuid")] impl FromSql for uuid::Uuid { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { value .as_blob() @@ -206,6 +206,7 @@ impl FromSql for uuid::Uuid { } impl<T: FromSql> FromSql for Option<T> { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { match value { ValueRef::Null => Ok(None), @@ -215,6 +216,7 @@ impl<T: FromSql> FromSql for Option<T> { } impl FromSql for Value { + #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { Ok(value.into()) } @@ -223,15 +225,11 @@ impl FromSql for Value { #[cfg(test)] mod test { use super::FromSql; - use crate::{Connection, Error}; - - fn checked_memory_handle() -> Connection { - Connection::open_in_memory().unwrap() - } + use crate::{Connection, Error, Result}; #[test] - fn test_integral_ranges() { - let db = checked_memory_handle(); + fn test_integral_ranges() -> Result<()> { + let db = Connection::open_in_memory()?; fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64]) where @@ -266,5 +264,6 @@ mod test { check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]); check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]); check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]); + Ok(()) } } |