From abb7d12087967bff099a3bcf1cb189376f49e0c1 Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Mon, 2 Sep 2019 17:18:33 -0700 Subject: [PATCH] Improve docs around errors --- src/connection.rs | 15 ++++++++++----- src/error.rs | 39 +++++++++++++++++++++++++++++++------- src/lib.rs | 1 + src/postgres/connection.rs | 4 ++-- src/postgres/error.rs | 6 +++--- src/postgres/mod.rs | 2 +- 6 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/connection.rs b/src/connection.rs index 539ed831..343473af 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -8,9 +8,8 @@ use crate::{ use crossbeam_queue::SegQueue; use crossbeam_utils::atomic::AtomicCell; use futures_channel::oneshot::{channel, Sender}; -use futures_util::TryFutureExt; use futures_core::{future::BoxFuture, stream::BoxStream}; -use futures_util::stream::StreamExt; +use futures_util::{stream::StreamExt, TryFutureExt}; use std::{ io, ops::{Deref, DerefMut}, @@ -37,13 +36,19 @@ pub trait RawConnection: Send { /// /// This method is not required to be called. A database server will eventually notice /// and clean up not fully closed connections. - /// + /// /// It is safe to close an already closed connection. fn close<'c>(&'c mut self) -> BoxFuture<'c, Result<(), Error>>; /// Verifies a connection to the database is still alive. fn ping<'c>(&'c mut self) -> BoxFuture<'c, Result<(), Error>> { - Box::pin(self.execute("SELECT 1", ::QueryParameters::new()).map_ok(|_| ())) + Box::pin( + self.execute( + "SELECT 1", + ::QueryParameters::new(), + ) + .map_ok(|_| ()), + ) } fn execute<'c>( @@ -103,7 +108,7 @@ where /// /// This method is not required to be called. A database server will eventually notice /// and clean up not fully closed connections. - /// + /// /// It is safe to close an already closed connection. pub async fn close(&self) -> crate::Result<()> { let mut conn = self.acquire().await; diff --git a/src/error.rs b/src/error.rs index 97914205..fb6ce3d2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,8 +4,10 @@ use std::{ io, }; +/// A convenient Result instantiation appropriate for SQLx. pub type Result = std::result::Result; +/// A generic error that represents all the ways a method can fail inside of SQLx. #[derive(Debug)] pub enum Error { /// Error communicating with the database backend. @@ -24,23 +26,46 @@ pub enum Error { Io(io::Error), /// An error was returned by the database backend. - Database(Box), + Database(Box), /// No rows were returned by a query expected to return at least one row. NotFound, + /// More than one row was returned by a query expected to return exactly one row. + FoundMoreThanOne, + // TODO: Remove and replace with `#[non_exhaustive]` when possible #[doc(hidden)] __Nonexhaustive, } -// TODO: Forward causes where present -impl StdError for Error {} +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::Io(error) => Some(error), + + _ => None + } + } +} -// TODO: Don't just forward to debug impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + match self { + Error::Io(error) => write!(f, "{}", error), + + Error::Database(error) => f.write_str(error.message()), + + Error::NotFound => f.write_str("found no rows when we expected at least one"), + + Error::FoundMoreThanOne => { + f.write_str("found more than one row when we expected exactly one") + } + + Error::__Nonexhaustive => { + unreachable!() + } + } } } @@ -53,7 +78,7 @@ impl From for Error { impl From for Error where - T: 'static + DbError, + T: 'static + DatabaseError, { #[inline] fn from(err: T) -> Self { @@ -62,7 +87,7 @@ where } /// An error that was returned by the database backend. -pub trait DbError: Debug + Send + Sync { +pub trait DatabaseError: Debug + Send + Sync { fn message(&self) -> &str; // TODO: Expose more error properties diff --git a/src/lib.rs b/src/lib.rs index c98a8795..ea8bca51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,7 @@ pub mod serialize; mod sql; pub mod types; +#[doc(inline)] pub use self::{ connection::Connection, error::{Error, Result}, diff --git a/src/postgres/connection.rs b/src/postgres/connection.rs index 75f328bb..4d59cb8a 100644 --- a/src/postgres/connection.rs +++ b/src/postgres/connection.rs @@ -1,6 +1,6 @@ use super::{ protocol::{self, Decode, Encode, Message, Terminate}, - Postgres, PostgresError, PostgresQueryParameters, PostgresRow, + Postgres, PostgresDatabaseError, PostgresQueryParameters, PostgresRow, }; use crate::{ connection::RawConnection, @@ -200,7 +200,7 @@ impl PostgresRawConnection { Message::Response(body) => { if body.severity().is_error() { // This is an error, stop the world and bubble as an error - return Err(PostgresError(body).into()); + return Err(PostgresDatabaseError(body).into()); } else { // This is a _warning_ // TODO: Do we *want* to do anything with these diff --git a/src/postgres/error.rs b/src/postgres/error.rs index 4fd098ea..8be36995 100644 --- a/src/postgres/error.rs +++ b/src/postgres/error.rs @@ -1,10 +1,10 @@ use super::protocol::Response; -use crate::error::DbError; +use crate::error::DatabaseError; #[derive(Debug)] -pub struct PostgresError(pub(super) Box); +pub struct PostgresDatabaseError(pub(super) Box); -impl DbError for PostgresError { +impl DatabaseError for PostgresDatabaseError { fn message(&self) -> &str { self.0.message() } diff --git a/src/postgres/mod.rs b/src/postgres/mod.rs index ee9071d4..33a4a0f7 100644 --- a/src/postgres/mod.rs +++ b/src/postgres/mod.rs @@ -7,7 +7,7 @@ mod row; pub mod types; pub use self::{ - backend::Postgres, connection::PostgresRawConnection, error::PostgresError, + backend::Postgres, connection::PostgresRawConnection, error::PostgresDatabaseError, query::PostgresQueryParameters, row::PostgresRow, };