diff --git a/sqlx-core/src/decode.rs b/sqlx-core/src/decode.rs index 3381fa79..f1551a23 100644 --- a/sqlx-core/src/decode.rs +++ b/sqlx-core/src/decode.rs @@ -6,12 +6,15 @@ use crate::database::HasRawValue; use crate::Database; /// A type that can be decoded from a SQL value. -pub trait Decode<'r, Db: Database>: Sized + Send + Sync { - fn decode(value: >::RawValue) -> Result; +pub trait Decode<'r, Db: Database>: Send + Sync { + fn decode(value: >::RawValue) -> Result + where + Self: Sized; } /// A type that can be decoded from a SQL value, without borrowing any data /// from the row. +#[allow(clippy::module_name_repetitions)] pub trait DecodeOwned: for<'r> Decode<'r, Db> {} impl DecodeOwned for T where T: for<'r> Decode<'r, Db> {} @@ -33,6 +36,13 @@ pub enum Error { Custom(Box), } +impl Error { + #[doc(hidden)] + pub fn msg(msg: D) -> Self { + Self::Custom(msg.to_string().into()) + } +} + impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { diff --git a/sqlx-core/src/encode.rs b/sqlx-core/src/encode.rs index 81df1480..a071a608 100644 --- a/sqlx-core/src/encode.rs +++ b/sqlx-core/src/encode.rs @@ -6,14 +6,8 @@ use crate::Database; /// A type that can be encoded into a SQL value. pub trait Encode: Send + Sync { - /// Encode this value into a SQL value. + /// Encode this value into the specified SQL type. fn encode(&self, ty: &Db::TypeInfo, out: &mut >::Output) -> Result<()>; - - #[doc(hidden)] - #[inline] - fn __type_name(&self) -> &'static str { - std::any::type_name::() - } } impl, Db: Database> Encode for &T { @@ -31,6 +25,13 @@ pub enum Error { Custom(Box), } +impl Error { + #[doc(hidden)] + pub fn msg(msg: D) -> Self { + Self::Custom(msg.to_string().into()) + } +} + impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { diff --git a/sqlx-core/src/lib.rs b/sqlx-core/src/lib.rs index 8bd02687..04b75f8e 100644 --- a/sqlx-core/src/lib.rs +++ b/sqlx-core/src/lib.rs @@ -19,6 +19,7 @@ #![allow(clippy::clippy::missing_errors_doc)] mod acquire; +mod arguments; mod close; mod column; mod connect; @@ -32,6 +33,8 @@ mod options; mod query_result; pub mod row; mod runtime; +mod r#type; +mod type_info; #[doc(hidden)] pub mod io; @@ -47,6 +50,7 @@ pub mod mock; pub mod blocking; pub use acquire::Acquire; +pub use arguments::{Argument, Arguments}; #[cfg(feature = "blocking")] pub use blocking::runtime::Blocking; pub use close::Close; @@ -60,6 +64,7 @@ pub use error::{DatabaseError, Error, Result}; pub use executor::Executor; pub use options::ConnectOptions; pub use query_result::QueryResult; +pub use r#type::{Type, TypeEncode, TypeDecode}; pub use row::Row; #[cfg(feature = "actix")] pub use runtime::Actix; @@ -70,3 +75,4 @@ pub use runtime::AsyncStd; pub use runtime::Runtime; #[cfg(feature = "tokio")] pub use runtime::Tokio; +pub use type_info::TypeInfo; diff --git a/sqlx-core/src/type.rs b/sqlx-core/src/type.rs new file mode 100644 index 00000000..20f79e28 --- /dev/null +++ b/sqlx-core/src/type.rs @@ -0,0 +1,52 @@ +use crate::{Database, Decode, Encode, TypeInfo}; + +// NOTE: The interface here is not final. There are some special considerations +// for MSSQL and Postgres (Arrays and Ranges) that need careful handling +// to ensure we correctly cover them. + +/// Indicates that a SQL type is supported for a database. +pub trait Type { + /// Returns the canonical SQL type identifier for this Rust type. + /// + /// When binding arguments, this is used to tell the database what is about to be sent; which, + /// the database then uses to guide query plans. This can be overridden by [`type_id_of`]. + /// + /// A map of SQL types to Rust types is populated with this and used + /// to determine the type that is returned from the anonymous struct type from [`query!`]. + /// + fn type_id() -> Db::TypeId + where + Self: Sized; + + /// Determines if this Rust type is compatible with the specified SQL type. + /// + /// To be compatible, the Rust type must support encoding _and_ decoding + /// from the specified SQL type. + /// + fn compatible(ty: &Db::TypeInfo) -> bool + where + Self: Sized, + { + ty.id() == Self::type_id() + } +} + +#[allow(clippy::module_name_repetitions)] +pub trait TypeEncode: Type + Encode { + /// Returns the SQL type identifier that best hints to the database + /// at the incoming value for a bind parameter. + #[allow(unused_variables)] + fn type_id_of(&self, ty: &Db::TypeInfo) -> Db::TypeId; + + /// Returns the Rust type name of this. + #[doc(hidden)] + #[inline] + fn __rust_type_name_of(&self) -> &'static str { + std::any::type_name::() + } +} + +#[allow(clippy::module_name_repetitions)] +pub trait TypeDecode<'r, Db: Database>: Type + Decode<'r, Db> {} + +impl<'r, T: Type + Decode<'r, Db>, Db: Database> TypeDecode<'r, Db> for T {}