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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! This module defines a wrapper for sqlx's Executor
//!
//! Unlike sqlx's Executor which provides several separate methods for different querying strategies,
//! our [`Executor`] has a single method which is generic using the [`QueryStrategy`] trait.

use std::future::Future;

use futures::future::BoxFuture;
use rorm_sql::value::Value;
use rorm_sql::DBImpl;

use crate::transaction::{Transaction, TransactionGuard};
use crate::{internal, Database, Error};

/// [`QueryStrategy`] returning nothing
///
/// `type Result<'result> = impl Future<Output = Result<(), Error>>`
pub struct Nothing;
impl QueryStrategy for Nothing {}

/// [`QueryStrategy`] returning how many rows have been affected by the query
///
/// `type Result<'result> = impl Future<Output = Result<u64, Error>>`
pub struct AffectedRows;
impl QueryStrategy for AffectedRows {}

/// [`QueryStrategy`] returning a single row
///
/// `type Result<'result> = impl Future<Output = Result<Row, Error>>`
pub struct One;
impl QueryStrategy for One {}

/// [`QueryStrategy`] returning an optional row
///
/// `type Result<'result> = impl Future<Output = Result<Option<Row>, Error>>`
pub struct Optional;
impl QueryStrategy for Optional {}

/// [`QueryStrategy`] returning a vector of rows
///
/// `type Result<'result> = impl Future<Output = Result<Vec<Row>, Error>>`
pub struct All;
impl QueryStrategy for All {}

/// [`QueryStrategy`] returning a stream of rows
///
/// `type Result<'result> = impl Stream<Item = Result<Row, Error>>`
pub struct Stream;
impl QueryStrategy for Stream {}

/// Define how a query is send to and results retrieved from the database.
///
/// This trait is implemented on the following unit structs:
/// - [`Nothing`] retrieves nothing
/// - [`Optional`] retrieves an optional row
/// - [`One`] retrieves a single row
/// - [`Stream`] retrieves many rows in a stream
/// - [`All`] retrieves many rows in a vector
/// - [`AffectedRows`] returns the number of rows affected by the query
///
/// This trait has an associated `Result<'result>` type which is returned by [`Executor::execute`].
/// To avoid boxing, these types are quite big.
///
/// Each of those unit structs' docs (follow links above) contains an easy to read `impl Trait` version of the actual types.
pub trait QueryStrategy: QueryStrategyResult + internal::executor::QueryStrategyImpl {}

/// Helper trait to make the `Result<'result>` public,
/// while keeping [`QueryStrategyImpl`](internal::executor::QueryStrategyImpl) itself private
#[doc(hidden)]
pub trait QueryStrategyResult {
    type Result<'result>;
}

/// Some kind of database connection which can execute queries
///
/// This trait is implemented by the database connection itself as well as transactions.
pub trait Executor<'executor> {
    /// Execute a query
    ///
    /// ```skipped
    /// db.execute::<All>("SELECT * FROM foo;".to_string(), vec![]);
    /// ```
    fn execute<'data, 'result, Q>(
        self,
        query: String,
        values: Vec<Value<'data>>,
    ) -> Q::Result<'result>
    where
        'executor: 'result,
        'data: 'result,
        Q: QueryStrategy;

    /// Get the executor's sql dialect.
    fn dialect(&self) -> DBImpl;

    /// A future producing a [`TransactionGuard`] returned by [`ensure_transaction`](Executor::ensure_transaction)
    type EnsureTransactionFuture: Future<Output = Result<TransactionGuard<'executor>, Error>> + Send;

    /// Ensure a piece of code is run inside a transaction using a [`TransactionGuard`].
    ///
    /// In generic code an [`Executor`] might and might not be a [`&mut Transaction`].
    /// But sometimes you'd want to ensure your code is run inside a transaction
    /// (for example [bulk inserts](crate::database::insert_bulk)).
    ///
    /// This method solves this by producing a type which is either an owned or borrowed Transaction
    /// depending on the [`Executor`] it is called on.
    fn ensure_transaction(self)
        -> BoxFuture<'executor, Result<TransactionGuard<'executor>, Error>>;
}

/// Choose whether to use transactions or not at runtime
///
/// Like a `Box<dyn Executor<'executor>>`
pub enum DynamicExecutor<'executor> {
    /// Use a default database connection
    Database(&'executor Database),
    /// Use a transaction
    Transaction(&'executor mut Transaction),
}
impl<'executor> Executor<'executor> for DynamicExecutor<'executor> {
    fn execute<'data, 'result, Q>(
        self,
        query: String,
        values: Vec<Value<'data>>,
    ) -> Q::Result<'result>
    where
        'executor: 'result,
        'data: 'result,
        Q: QueryStrategy,
    {
        match self {
            DynamicExecutor::Database(db) => db.execute::<Q>(query, values),
            DynamicExecutor::Transaction(tr) => tr.execute::<Q>(query, values),
        }
    }

    fn dialect(&self) -> DBImpl {
        match self {
            DynamicExecutor::Database(db) => db.dialect(),
            DynamicExecutor::Transaction(tr) => tr.dialect(),
        }
    }

    type EnsureTransactionFuture = BoxFuture<'executor, Result<TransactionGuard<'executor>, Error>>;

    fn ensure_transaction(self) -> Self::EnsureTransactionFuture {
        match self {
            DynamicExecutor::Database(db) => db.ensure_transaction(),
            DynamicExecutor::Transaction(tr) => Box::pin(tr.ensure_transaction()),
        }
    }
}