mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-10-31 13:58:55 +08:00 
			
		
		
		
	Merge pull request #1313 from itsxaos/nonzero
Implement FromSql & ToSql for std::num::NonZero types
This commit is contained in:
		| @@ -96,6 +96,15 @@ macro_rules! from_sql_integral( | ||||
|                 i.try_into().map_err(|_| FromSqlError::OutOfRange(i)) | ||||
|             } | ||||
|         } | ||||
|     ); | ||||
|     (non_zero $nz:ty, $z:ty) => ( | ||||
|         impl FromSql for $nz { | ||||
|             #[inline] | ||||
|             fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { | ||||
|                 let i = <$z>::column_result(value)?; | ||||
|                 <$nz>::new(i).ok_or(FromSqlError::OutOfRange(0)) | ||||
|             } | ||||
|         } | ||||
|     ) | ||||
| ); | ||||
|  | ||||
| @@ -110,6 +119,22 @@ from_sql_integral!(u32); | ||||
| from_sql_integral!(u64); | ||||
| from_sql_integral!(usize); | ||||
|  | ||||
| from_sql_integral!(non_zero std::num::NonZeroIsize, isize); | ||||
| from_sql_integral!(non_zero std::num::NonZeroI8, i8); | ||||
| from_sql_integral!(non_zero std::num::NonZeroI16, i16); | ||||
| from_sql_integral!(non_zero std::num::NonZeroI32, i32); | ||||
| from_sql_integral!(non_zero std::num::NonZeroI64, i64); | ||||
| #[cfg(feature = "i128_blob")] | ||||
| #[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))] | ||||
| from_sql_integral!(non_zero std::num::NonZeroI128, i128); | ||||
|  | ||||
| from_sql_integral!(non_zero std::num::NonZeroUsize, usize); | ||||
| from_sql_integral!(non_zero std::num::NonZeroU8, u8); | ||||
| from_sql_integral!(non_zero std::num::NonZeroU16, u16); | ||||
| from_sql_integral!(non_zero std::num::NonZeroU32, u32); | ||||
| from_sql_integral!(non_zero std::num::NonZeroU64, u64); | ||||
| // std::num::NonZeroU128 is not supported since u128 isn't either | ||||
|  | ||||
| impl FromSql for i64 { | ||||
|     #[inline] | ||||
|     fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { | ||||
| @@ -273,4 +298,70 @@ mod test { | ||||
|         check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_nonzero_ranges() -> Result<()> { | ||||
|         let db = Connection::open_in_memory()?; | ||||
|  | ||||
|         macro_rules! check_ranges { | ||||
|             ($nz:ty, $out_of_range:expr, $in_range:expr) => { | ||||
|                 for &n in $out_of_range { | ||||
|                     assert_eq!( | ||||
|                         db.query_row("SELECT ?1", [n], |r| r.get::<_, $nz>(0)), | ||||
|                         Err(Error::IntegralValueOutOfRange(0, n)), | ||||
|                         "{}", | ||||
|                         std::any::type_name::<$nz>() | ||||
|                     ); | ||||
|                 } | ||||
|                 for &n in $in_range { | ||||
|                     let non_zero = <$nz>::new(n).unwrap(); | ||||
|                     assert_eq!( | ||||
|                         Ok(non_zero), | ||||
|                         db.query_row("SELECT ?1", [non_zero], |r| r.get::<_, $nz>(0)) | ||||
|                     ); | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         check_ranges!(std::num::NonZeroI8, &[0, -129, 128], &[-128, 1, 127]); | ||||
|         check_ranges!( | ||||
|             std::num::NonZeroI16, | ||||
|             &[0, -32769, 32768], | ||||
|             &[-32768, -1, 1, 32767] | ||||
|         ); | ||||
|         check_ranges!( | ||||
|             std::num::NonZeroI32, | ||||
|             &[0, -2_147_483_649, 2_147_483_648], | ||||
|             &[-2_147_483_648, -1, 1, 2_147_483_647] | ||||
|         ); | ||||
|         check_ranges!( | ||||
|             std::num::NonZeroI64, | ||||
|             &[0], | ||||
|             &[-2_147_483_648, -1, 1, 2_147_483_647, i64::MAX, i64::MIN] | ||||
|         ); | ||||
|         check_ranges!( | ||||
|             std::num::NonZeroIsize, | ||||
|             &[0], | ||||
|             &[-2_147_483_648, -1, 1, 2_147_483_647] | ||||
|         ); | ||||
|         check_ranges!(std::num::NonZeroU8, &[0, -2, -1, 256], &[1, 255]); | ||||
|         check_ranges!(std::num::NonZeroU16, &[0, -2, -1, 65536], &[1, 65535]); | ||||
|         check_ranges!( | ||||
|             std::num::NonZeroU32, | ||||
|             &[0, -2, -1, 4_294_967_296], | ||||
|             &[1, 4_294_967_295] | ||||
|         ); | ||||
|         check_ranges!( | ||||
|             std::num::NonZeroU64, | ||||
|             &[0, -2, -1, -4_294_967_296], | ||||
|             &[1, 4_294_967_295, i64::MAX as u64] | ||||
|         ); | ||||
|         check_ranges!( | ||||
|             std::num::NonZeroUsize, | ||||
|             &[0, -2, -1, -4_294_967_296], | ||||
|             &[1, 4_294_967_295] | ||||
|         ); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -26,7 +26,8 @@ | ||||
| //! [`ToSql`] always succeeds except when storing a `u64` or `usize` value that | ||||
| //! cannot fit in an `INTEGER` (`i64`). Also note that SQLite ignores column | ||||
| //! types, so if you store an `i64` in a column with type `REAL` it will be | ||||
| //! stored as an `INTEGER`, not a `REAL`. | ||||
| //! stored as an `INTEGER`, not a `REAL` (unless the column is part of a | ||||
| //! [STRICT table](https://www.sqlite.org/stricttables.html)). | ||||
| //! | ||||
| //! If the `time` feature is enabled, implementations are | ||||
| //! provided for `time::OffsetDateTime` that use the RFC 3339 date/time format, | ||||
|   | ||||
| @@ -51,6 +51,12 @@ macro_rules! from_value( | ||||
|             #[inline] | ||||
|             fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())} | ||||
|         } | ||||
|     ); | ||||
|     (non_zero $t:ty) => ( | ||||
|         impl From<$t> for ToSqlOutput<'_> { | ||||
|             #[inline] | ||||
|             fn from(t: $t) -> Self { ToSqlOutput::Owned(t.get().into())} | ||||
|         } | ||||
|     ) | ||||
| ); | ||||
| from_value!(String); | ||||
| @@ -68,6 +74,15 @@ from_value!(f32); | ||||
| from_value!(f64); | ||||
| from_value!(Vec<u8>); | ||||
|  | ||||
| from_value!(non_zero std::num::NonZeroI8); | ||||
| from_value!(non_zero std::num::NonZeroI16); | ||||
| from_value!(non_zero std::num::NonZeroI32); | ||||
| from_value!(non_zero std::num::NonZeroI64); | ||||
| from_value!(non_zero std::num::NonZeroIsize); | ||||
| from_value!(non_zero std::num::NonZeroU8); | ||||
| from_value!(non_zero std::num::NonZeroU16); | ||||
| from_value!(non_zero std::num::NonZeroU32); | ||||
|  | ||||
| // It would be nice if we could avoid the heap allocation (of the `Vec`) that | ||||
| // `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not | ||||
| // worth adding another case to Value. | ||||
| @@ -75,6 +90,10 @@ from_value!(Vec<u8>); | ||||
| #[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))] | ||||
| from_value!(i128); | ||||
|  | ||||
| #[cfg(feature = "i128_blob")] | ||||
| #[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))] | ||||
| from_value!(non_zero std::num::NonZeroI128); | ||||
|  | ||||
| #[cfg(feature = "uuid")] | ||||
| #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] | ||||
| from_value!(uuid::Uuid); | ||||
| @@ -165,10 +184,23 @@ to_sql_self!(u32); | ||||
| to_sql_self!(f32); | ||||
| to_sql_self!(f64); | ||||
|  | ||||
| to_sql_self!(std::num::NonZeroI8); | ||||
| to_sql_self!(std::num::NonZeroI16); | ||||
| to_sql_self!(std::num::NonZeroI32); | ||||
| to_sql_self!(std::num::NonZeroI64); | ||||
| to_sql_self!(std::num::NonZeroIsize); | ||||
| to_sql_self!(std::num::NonZeroU8); | ||||
| to_sql_self!(std::num::NonZeroU16); | ||||
| to_sql_self!(std::num::NonZeroU32); | ||||
|  | ||||
| #[cfg(feature = "i128_blob")] | ||||
| #[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))] | ||||
| to_sql_self!(i128); | ||||
|  | ||||
| #[cfg(feature = "i128_blob")] | ||||
| #[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))] | ||||
| to_sql_self!(std::num::NonZeroI128); | ||||
|  | ||||
| #[cfg(feature = "uuid")] | ||||
| #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] | ||||
| to_sql_self!(uuid::Uuid); | ||||
| @@ -186,12 +218,27 @@ macro_rules! to_sql_self_fallible( | ||||
|                 ))) | ||||
|             } | ||||
|         } | ||||
|     ); | ||||
|     (non_zero $t:ty) => ( | ||||
|         impl ToSql for $t { | ||||
|             #[inline] | ||||
|             fn to_sql(&self) -> Result<ToSqlOutput<'_>> { | ||||
|                 Ok(ToSqlOutput::Owned(Value::Integer( | ||||
|                     i64::try_from(self.get()).map_err( | ||||
|                         // TODO: Include the values in the error message. | ||||
|                         |err| Error::ToSqlConversionFailure(err.into()) | ||||
|                     )? | ||||
|                 ))) | ||||
|             } | ||||
|         } | ||||
|     ) | ||||
| ); | ||||
|  | ||||
| // Special implementations for usize and u64 because these conversions can fail. | ||||
| to_sql_self_fallible!(u64); | ||||
| to_sql_self_fallible!(usize); | ||||
| to_sql_self_fallible!(non_zero std::num::NonZeroU64); | ||||
| to_sql_self_fallible!(non_zero std::num::NonZeroUsize); | ||||
|  | ||||
| impl<T: ?Sized> ToSql for &'_ T | ||||
| where | ||||
| @@ -267,9 +314,26 @@ mod test { | ||||
|         is_to_sql::<i16>(); | ||||
|         is_to_sql::<i32>(); | ||||
|         is_to_sql::<i64>(); | ||||
|         is_to_sql::<isize>(); | ||||
|         is_to_sql::<u8>(); | ||||
|         is_to_sql::<u16>(); | ||||
|         is_to_sql::<u32>(); | ||||
|         is_to_sql::<u64>(); | ||||
|         is_to_sql::<usize>(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_nonzero_types() { | ||||
|         is_to_sql::<std::num::NonZeroI8>(); | ||||
|         is_to_sql::<std::num::NonZeroI16>(); | ||||
|         is_to_sql::<std::num::NonZeroI32>(); | ||||
|         is_to_sql::<std::num::NonZeroI64>(); | ||||
|         is_to_sql::<std::num::NonZeroIsize>(); | ||||
|         is_to_sql::<std::num::NonZeroU8>(); | ||||
|         is_to_sql::<std::num::NonZeroU16>(); | ||||
|         is_to_sql::<std::num::NonZeroU32>(); | ||||
|         is_to_sql::<std::num::NonZeroU64>(); | ||||
|         is_to_sql::<std::num::NonZeroUsize>(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -398,6 +462,54 @@ mod test { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "i128_blob")] | ||||
|     #[test] | ||||
|     fn test_non_zero_i128() -> crate::Result<()> { | ||||
|         use std::num::NonZeroI128; | ||||
|         macro_rules! nz { | ||||
|             ($x:expr) => { | ||||
|                 NonZeroI128::new($x).unwrap() | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         let db = crate::Connection::open_in_memory()?; | ||||
|         db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?; | ||||
|         db.execute( | ||||
|             "INSERT INTO foo(i128, desc) VALUES | ||||
|                 (?1, 'neg one'), (?2, 'neg two'), | ||||
|                 (?3, 'pos one'), (?4, 'pos two'), | ||||
|                 (?5, 'min'), (?6, 'max')", | ||||
|             [ | ||||
|                 nz!(-1), | ||||
|                 nz!(-2), | ||||
|                 nz!(1), | ||||
|                 nz!(2), | ||||
|                 nz!(i128::MIN), | ||||
|                 nz!(i128::MAX), | ||||
|             ], | ||||
|         )?; | ||||
|         let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?; | ||||
|  | ||||
|         let res = stmt | ||||
|             .query_map([], |row| Ok((row.get(0)?, row.get(1)?)))? | ||||
|             .collect::<Result<Vec<(NonZeroI128, String)>, _>>()?; | ||||
|  | ||||
|         assert_eq!( | ||||
|             res, | ||||
|             &[ | ||||
|                 (nz!(i128::MIN), "min".to_owned()), | ||||
|                 (nz!(-2), "neg two".to_owned()), | ||||
|                 (nz!(-1), "neg one".to_owned()), | ||||
|                 (nz!(1), "pos one".to_owned()), | ||||
|                 (nz!(2), "pos two".to_owned()), | ||||
|                 (nz!(i128::MAX), "max".to_owned()), | ||||
|             ] | ||||
|         ); | ||||
|         let err = db.query_row("SELECT ?1", [0i128], |row| row.get::<_, NonZeroI128>(0)); | ||||
|         assert_eq!(err, Err(crate::Error::IntegralValueOutOfRange(0, 0))); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "uuid")] | ||||
|     #[test] | ||||
|     fn test_uuid() -> crate::Result<()> { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user