1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
use backend::Backend;
use query_builder::AsQuery;
use query_source::QuerySource;

pub trait InternalBoxedDsl<'a, DB: Backend> {
    type Output;

    fn internal_into_boxed(self) -> Self::Output;
}

impl<'a, T, DB> InternalBoxedDsl<'a, DB> for T where
    DB: Backend,
    T: QuerySource + AsQuery,
    T::Query: InternalBoxedDsl<'a, DB>,
{
    type Output = <T::Query as InternalBoxedDsl<'a, DB>>::Output;

    fn internal_into_boxed(self) -> Self::Output {
        self.as_query().internal_into_boxed()
    }
}

/// Boxes the pieces of a query into a single type. This is useful for cases
/// where you want to conditionally modify a query, but need the type to remain
/// the same. The backend must be specified as part of this. It is not possible
/// to box a query and have it be useable on multiple backends.
///
/// A boxed query will incur a minor performance penalty, as the query builder
/// can no longer be inlined by the compiler. For most applications this cost
/// will be minimal.
///
/// ### Example
///
/// ```rust
/// # #[macro_use] extern crate diesel;
/// # include!("src/doctest_setup.rs");
/// #
/// # table! {
/// #     users {
/// #         id -> Integer,
/// #         name -> VarChar,
/// #     }
/// # }
/// #
/// # fn main() {
/// #     use std::collections::HashMap;
/// #     let connection = establish_connection();
/// #     let mut params = HashMap::new();
/// #     params.insert("name", "Sean");
/// let mut query = users::table.into_boxed();
/// if let Some(name) = params.get("name") {
///     query = query.filter(users::name.eq(name));
/// }
/// let users = query.load(&connection);
/// #     let expected = vec![(1, String::from("Sean"))];
/// #     assert_eq!(Ok(expected), users);
/// # }
/// ```
///
/// Diesel queries also have a similar problem to [`Iterator`][iterator], where
/// returning them from a function requires exposing the implementation of that
/// function. The [`helper_types`][helper_types] module exists to help with this,
/// but you might want to hide the return type or have it conditionally change.
/// Boxing can achieve both.
///
/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html
/// [helper_types]: ../helper_types/index.html
///
/// ### Example
///
/// ```rust
/// # #[macro_use] extern crate diesel;
/// # include!("src/doctest_setup.rs");
/// #
/// # table! {
/// #     users {
/// #         id -> Integer,
/// #         name -> VarChar,
/// #     }
/// # }
/// #
/// # fn main() {
/// #     let connection = establish_connection();
/// fn users_by_name<'a>(name: &'a str) -> users::BoxedQuery<'a, DB> {
///     users::table.filter(users::name.eq(name)).into_boxed()
/// }
///
/// assert_eq!(Ok(1), users_by_name("Sean").select(users::id).first(&connection));
/// assert_eq!(Ok(2), users_by_name("Tess").select(users::id).first(&connection));
/// # }
/// ```
pub trait BoxedDsl: Sized {
    fn into_boxed<'a, DB>(self) -> Self::Output where
        DB: Backend,
        Self: InternalBoxedDsl<'a, DB>,
    {
        self.internal_into_boxed()
    }
}

impl<T: AsQuery> BoxedDsl for T {}