refactor: remove several blanket impls to allow more customization

This commit is contained in:
Ryan Leckey 2020-06-27 04:05:23 -07:00
parent 34859af1d3
commit eb26e9f557
13 changed files with 104 additions and 62 deletions

View File

@ -2,7 +2,6 @@
use crate::database::{Database, HasValueRef};
use crate::error::BoxDynError;
use crate::value::ValueRef;
/// A type that can be decoded from the database.
///
@ -62,17 +61,24 @@ pub trait Decode<'r, DB: Database>: Sized {
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError>;
}
// implement `Decode` for Option<T> for all SQL types
impl<'r, DB, T> Decode<'r, DB> for Option<T>
where
DB: Database,
T: Decode<'r, DB>,
{
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> {
#[allow(unused_macros)]
macro_rules! impl_decode_for_option {
($DB:ident) => {
impl<'r, T> crate::decode::Decode<'r, $DB> for Option<T>
where
T: crate::decode::Decode<'r, $DB>,
{
fn decode(
value: <$DB as crate::database::HasValueRef<'r>>::ValueRef,
) -> Result<Self, crate::error::BoxDynError> {
use crate::value::ValueRef;
if value.is_null() {
Ok(None)
} else {
Ok(Some(T::decode(value)?))
}
}
}
};
}

View File

@ -45,22 +45,32 @@ pub trait Encode<'q, DB: Database> {
}
}
impl<'q, T, DB: Database> Encode<'q, DB> for &'_ T
where
T: Encode<'q, DB>,
{
// de-generified using macros because Any doesn't want this
#[allow(unused_macros)]
macro_rules! impl_encode_for_ref {
($DB:ident) => {
impl<'q, T> crate::encode::Encode<'q, $DB> for &'_ T
where
T: crate::encode::Encode<'q, $DB>,
{
#[inline]
fn encode(self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
<T as Encode<DB>>::encode_by_ref(self, buf)
fn encode(
self,
buf: &mut <$DB as crate::database::HasArguments<'q>>::ArgumentBuffer,
) -> crate::encode::IsNull {
<T as crate::encode::Encode<$DB>>::encode_by_ref(self, buf)
}
#[inline]
fn encode_by_ref(&self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
<&T as Encode<DB>>::encode(self, buf)
fn encode_by_ref(
&self,
buf: &mut <$DB as crate::database::HasArguments<'q>>::ArgumentBuffer,
) -> crate::encode::IsNull {
<&T as crate::encode::Encode<$DB>>::encode(self, buf)
}
#[inline]
fn produces(&self) -> Option<DB::TypeInfo> {
fn produces(&self) -> Option<<$DB as crate::database::Database>::TypeInfo> {
(**self).produces()
}
@ -68,8 +78,11 @@ where
fn size_hint(&self) -> usize {
(**self).size_hint()
}
}
};
}
// de-generified using macros because MSSQL has a different concept of how nullable encoding works
#[allow(unused_macros)]
macro_rules! impl_encode_for_option {
($DB:ident) => {

View File

@ -31,5 +31,10 @@ impl_into_arguments_for_arguments!(MssqlArguments);
impl_executor_for_pool_connection!(Mssql, MssqlConnection, MssqlRow);
impl_executor_for_transaction!(Mssql, MssqlRow);
// required because of `Any`
impl_type_for_ref_and_option!(Mssql);
impl_encode_for_ref!(Mssql);
impl_decode_for_option!(Mssql);
// FIXME: RPC NULL parameter values / results
// FIXME: RPC Empty String parameter values

View File

@ -31,6 +31,11 @@ impl_into_arguments_for_arguments!(MySqlArguments);
impl_executor_for_pool_connection!(MySql, MySqlConnection, MySqlRow);
impl_executor_for_transaction!(MySql, MySqlRow);
// required because of `Any`
impl_type_for_ref_and_option!(MySql);
impl_encode_for_ref!(MySql);
impl_decode_for_option!(MySql);
// required because some databases have a different handling
// of NULL
impl_encode_for_option!(MySql);

View File

@ -46,7 +46,7 @@ impl Type<MySql> for Vec<u8> {
}
fn compatible(ty: &MySqlTypeInfo) -> bool {
<&[u8] as Type<MySql>>::compatible(ty)
<[u8] as Type<MySql>>::compatible(ty)
}
}

View File

@ -34,6 +34,11 @@ impl_into_arguments_for_arguments!(PgArguments);
impl_executor_for_pool_connection!(Postgres, PgConnection, PgRow);
impl_executor_for_transaction!(Postgres, PgRow);
// required because of `Any`
impl_type_for_ref_and_option!(Postgres);
impl_encode_for_ref!(Postgres);
impl_decode_for_option!(Postgres);
// required because some databases have a different handling
// of NULL
impl_encode_for_option!(Postgres);

View File

@ -37,7 +37,6 @@ impl<'q, T> Encode<'q, Postgres> for Vec<T>
where
for<'a> &'a [T]: Encode<'q, Postgres>,
T: Encode<'q, Postgres>,
Self: Type<Postgres>,
{
#[inline]
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
@ -48,7 +47,6 @@ where
impl<'q, T> Encode<'q, Postgres> for &'_ [T]
where
T: Encode<'q, Postgres> + Type<Postgres>,
Self: Type<Postgres>,
{
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
buf.extend(&1_i32.to_be_bytes()); // number of dimensions
@ -77,7 +75,6 @@ where
impl<'r, T> Decode<'r, Postgres> for Vec<T>
where
T: for<'a> Decode<'a, Postgres> + Type<Postgres>,
Self: Type<Postgres>,
{
fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
let element_type_info = T::type_info();

View File

@ -171,7 +171,7 @@ pub use range::PgRange;
pub use record::{PgRecordDecoder, PgRecordEncoder};
// Type::compatible impl appropriate for arrays
fn array_compatible<E: Type<Postgres>>(ty: &PgTypeInfo) -> bool {
fn array_compatible<E: ?Sized + Type<Postgres>>(ty: &PgTypeInfo) -> bool {
// we require the declared type to be an _array_ with an
// element type that is acceptable
if let PgTypeKind::Array(element) = &ty.kind() {

View File

@ -28,7 +28,7 @@ impl Type<Postgres> for [&'_ str] {
}
fn compatible(ty: &PgTypeInfo) -> bool {
array_compatible::<&str>(ty)
array_compatible::<str>(ty)
}
}
@ -64,11 +64,11 @@ impl<'r> Decode<'r, Postgres> for &'r str {
impl Type<Postgres> for String {
fn type_info() -> PgTypeInfo {
<&str as Type<Postgres>>::type_info()
<str as Type<Postgres>>::type_info()
}
fn compatible(ty: &PgTypeInfo) -> bool {
<&str as Type<Postgres>>::compatible(ty)
<str as Type<Postgres>>::compatible(ty)
}
}

View File

@ -35,6 +35,11 @@ impl_into_arguments_for_arguments!(SqliteArguments<'q>);
impl_executor_for_pool_connection!(Sqlite, SqliteConnection, SqliteRow);
impl_executor_for_transaction!(Sqlite, SqliteRow);
// required because of `Any`
impl_type_for_ref_and_option!(Sqlite);
impl_encode_for_ref!(Sqlite);
impl_decode_for_option!(Sqlite);
// required because some databases have a different handling
// of NULL
impl_encode_for_option!(Sqlite);

View File

@ -29,7 +29,7 @@ impl<'r> Decode<'r, Sqlite> for &'r [u8] {
impl Type<Sqlite> for Vec<u8> {
fn type_info() -> SqliteTypeInfo {
<&[u8] as Type<Sqlite>>::type_info()
<[u8] as Type<Sqlite>>::type_info()
}
}

View File

@ -29,7 +29,7 @@ impl<'r> Decode<'r, Sqlite> for &'r str {
impl Type<Sqlite> for String {
fn type_info() -> SqliteTypeInfo {
<&str as Type<Sqlite>>::type_info()
<str as Type<Sqlite>>::type_info()
}
}

View File

@ -69,24 +69,30 @@ pub trait Type<DB: Database> {
}
}
// for references, the underlying SQL type is identical
impl<T: ?Sized + Type<DB>, DB: Database> Type<DB> for &'_ T {
fn type_info() -> DB::TypeInfo {
<T as Type<DB>>::type_info()
// de-generified using macros because Any doesn't want this
#[allow(unused_macros)]
macro_rules! impl_type_for_ref_and_option {
($DB:ident) => {
// for references, the underlying SQL type is identical
impl<T: ?Sized + crate::types::Type<$DB>> crate::types::Type<$DB> for &'_ T {
fn type_info() -> <$DB as crate::database::Database>::TypeInfo {
<T as crate::types::Type<$DB>>::type_info()
}
fn compatible(ty: &DB::TypeInfo) -> bool {
<T as Type<DB>>::compatible(ty)
}
}
// for optionals, the underlying SQL type is identical
impl<T: Type<DB>, DB: Database> Type<DB> for Option<T> {
fn type_info() -> DB::TypeInfo {
<T as Type<DB>>::type_info()
}
fn compatible(ty: &DB::TypeInfo) -> bool {
<T as Type<DB>>::compatible(ty)
fn compatible(ty: &<$DB as crate::database::Database>::TypeInfo) -> bool {
<T as crate::types::Type<$DB>>::compatible(ty)
}
}
// for optionals, the underlying SQL type is identical
impl<T: crate::types::Type<$DB>> crate::types::Type<$DB> for Option<T> {
fn type_info() -> <$DB as crate::database::Database>::TypeInfo {
<T as crate::types::Type<$DB>>::type_info()
}
fn compatible(ty: &<$DB as crate::database::Database>::TypeInfo) -> bool {
<T as crate::types::Type<$DB>>::compatible(ty)
}
}
};
}