Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow chaining map / try_map #854

Merged
merged 2 commits into from
Nov 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion sqlx-core/src/any/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ impl_acquire!(Any, AnyConnection);
impl_column_index_for_row!(AnyRow);
impl_column_index_for_statement!(AnyStatement);
impl_into_maybe_pool!(Any, AnyConnection);
impl_map_row!(Any, AnyRow);

// required because some databases have a different handling of NULL
impl_encode_for_option!(Any);
1 change: 0 additions & 1 deletion sqlx-core/src/mssql/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ pub type MssqlPool = crate::pool::Pool<Mssql>;
impl_into_arguments_for_arguments!(MssqlArguments);
impl_executor_for_pool_connection!(Mssql, MssqlConnection, MssqlRow);
impl_executor_for_transaction!(Mssql, MssqlRow);
impl_map_row!(Mssql, MssqlRow);
impl_acquire!(Mssql, MssqlConnection);
impl_column_index_for_row!(MssqlRow);
impl_column_index_for_statement!(MssqlStatement);
Expand Down
1 change: 0 additions & 1 deletion sqlx-core/src/mysql/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ pub type MySqlPoolOptions = crate::pool::PoolOptions<MySql>;
impl_into_arguments_for_arguments!(MySqlArguments);
impl_executor_for_pool_connection!(MySql, MySqlConnection, MySqlRow);
impl_executor_for_transaction!(MySql, MySqlRow);
impl_map_row!(MySql, MySqlRow);
impl_acquire!(MySql, MySqlConnection);
impl_column_index_for_row!(MySqlRow);
impl_column_index_for_statement!(MySqlStatement);
Expand Down
1 change: 0 additions & 1 deletion sqlx-core/src/postgres/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ pub type PgPoolOptions = crate::pool::PoolOptions<Postgres>;
impl_into_arguments_for_arguments!(PgArguments);
impl_executor_for_pool_connection!(Postgres, PgConnection, PgRow);
impl_executor_for_transaction!(Postgres, PgRow);
impl_map_row!(Postgres, PgRow);
impl_acquire!(Postgres, PgConnection);
impl_column_index_for_row!(PgRow);
impl_column_index_for_statement!(PgStatement);
Expand Down
119 changes: 50 additions & 69 deletions sqlx-core/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,22 +114,26 @@ where
/// The [`query_as`](super::query_as::query_as) method will construct a mapped query using
/// a [`FromRow`](super::from_row::FromRow) implementation.
#[inline]
pub fn map<F, O>(self, f: F) -> Map<'q, DB, impl TryMapRow<DB, Output = O>, A>
pub fn map<F, O>(
self,
mut f: F,
) -> Map<'q, DB, impl FnMut(DB::Row) -> Result<O, Error> + Send, A>
where
F: MapRow<DB, Output = O>,
F: FnMut(DB::Row) -> O + Send,
O: Unpin,
{
self.try_map(MapRowAdapter(f))
self.try_map(move |row| Ok(f(row)))
}

/// Map each row in the result to another type.
///
/// The [`query_as`](super::query_as::query_as) method will construct a mapped query using
/// a [`FromRow`](super::from_row::FromRow) implementation.
#[inline]
pub fn try_map<F>(self, f: F) -> Map<'q, DB, F, A>
pub fn try_map<F, O>(self, f: F) -> Map<'q, DB, F, A>
where
F: TryMapRow<DB>,
F: FnMut(DB::Row) -> Result<O, Error> + Send,
O: Unpin,
{
Map {
inner: self,
Expand Down Expand Up @@ -251,10 +255,48 @@ where
impl<'q, DB, F, O, A> Map<'q, DB, F, A>
where
DB: Database,
F: TryMapRow<DB, Output = O>,
F: FnMut(DB::Row) -> Result<O, Error> + Send,
O: Send + Unpin,
A: 'q + Send + IntoArguments<'q, DB>,
{
/// Map each row in the result to another type.
///
/// See [`try_map`](Map::try_map) for a fallible version of this method.
///
/// The [`query_as`](super::query_as::query_as) method will construct a mapped query using
/// a [`FromRow`](super::from_row::FromRow) implementation.
#[inline]
pub fn map<G, P>(
self,
mut g: G,
) -> Map<'q, DB, impl FnMut(DB::Row) -> Result<P, Error> + Send, A>
where
G: FnMut(O) -> P + Send,
P: Unpin,
{
self.try_map(move |data| Ok(g(data)))
}

/// Map each row in the result to another type.
///
/// The [`query_as`](super::query_as::query_as) method will construct a mapped query using
/// a [`FromRow`](super::from_row::FromRow) implementation.
#[inline]
pub fn try_map<G, P>(
self,
mut g: G,
) -> Map<'q, DB, impl FnMut(DB::Row) -> Result<P, Error> + Send, A>
where
G: FnMut(O) -> Result<P, Error> + Send,
P: Unpin,
{
let mut f = self.mapper;
Map {
inner: self.inner,
mapper: move |row| f(row).and_then(|o| g(o)),
}
}

/// Execute the query and return the generated results as a stream.
pub fn fetch<'e, 'c: 'e, E>(self, executor: E) -> BoxStream<'e, Result<O, Error>>
where
Expand Down Expand Up @@ -294,7 +336,7 @@ where
r#yield!(match v {
Either::Left(v) => Either::Left(v),
Either::Right(row) => {
Either::Right(self.mapper.try_map_row(row)?)
Either::Right((self.mapper)(row)?)
}
});
}
Expand Down Expand Up @@ -344,47 +386,13 @@ where
let row = executor.fetch_optional(self.inner).await?;

if let Some(row) = row {
self.mapper.try_map_row(row).map(Some)
(self.mapper)(row).map(Some)
} else {
Ok(None)
}
}
}

// A (hopefully) temporary workaround for an internal compiler error (ICE) involving higher-ranked
// trait bounds (HRTBs), associated types and closures.
//
// See https://github.com/rust-lang/rust/issues/62529

pub trait TryMapRow<DB: Database>: Send {
type Output: Unpin;

fn try_map_row(&mut self, row: DB::Row) -> Result<Self::Output, Error>;
}

pub trait MapRow<DB: Database>: Send {
type Output: Unpin;

fn map_row(&mut self, row: DB::Row) -> Self::Output;
}

// A private adapter that implements [MapRow] in terms of [TryMapRow]
// Just ends up Ok wrapping it

struct MapRowAdapter<F>(F);

impl<DB: Database, O, F> TryMapRow<DB> for MapRowAdapter<F>
where
O: Unpin,
F: MapRow<DB, Output = O>,
{
type Output = O;

fn try_map_row(&mut self, row: DB::Row) -> Result<Self::Output, Error> {
Ok(self.0.map_row(row))
}
}

// Make a SQL query from a statement.
pub(crate) fn query_statement<'q, DB>(
statement: &'q <DB as HasStatement<'q>>::Statement,
Expand Down Expand Up @@ -443,30 +451,3 @@ where
persistent: true,
}
}

#[allow(unused_macros)]
macro_rules! impl_map_row {
($DB:ident, $R:ident) => {
impl<O: Unpin, F> crate::query::MapRow<$DB> for F
where
F: Send + FnMut($R) -> O,
{
type Output = O;

fn map_row(&mut self, row: $R) -> O {
(self)(row)
}
}

impl<O: Unpin, F> crate::query::TryMapRow<$DB> for F
where
F: Send + FnMut($R) -> Result<O, crate::error::Error>,
{
type Output = O;

fn try_map_row(&mut self, row: $R) -> Result<O, crate::error::Error> {
(self)(row)
}
}
};
}
1 change: 0 additions & 1 deletion sqlx-core/src/sqlite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ pub type SqlitePoolOptions = crate::pool::PoolOptions<Sqlite>;
impl_into_arguments_for_arguments!(SqliteArguments<'q>);
impl_executor_for_pool_connection!(Sqlite, SqliteConnection, SqliteRow);
impl_executor_for_transaction!(Sqlite, SqliteRow);
impl_map_row!(Sqlite, SqliteRow);
impl_column_index_for_row!(SqliteRow);
impl_column_index_for_statement!(SqliteStatement);
impl_acquire!(Sqlite, SqliteConnection);
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ pub use self::decode::Decode;
/// Types and traits for the `query` family of functions and macros.
pub mod query {
pub use sqlx_core::query::{Map, Query};
pub use sqlx_core::query::{MapRow, TryMapRow};
pub use sqlx_core::query_as::QueryAs;
pub use sqlx_core::query_scalar::QueryScalar;
}
Expand Down