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
//! This module contains the code for configuring the connection pool.

use std::str::FromStr;

use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod, Runtime};
use tokio_postgres::{
    tls::{MakeTlsConnect, TlsConnect},
    Config, NoTls, Socket,
};

/// An empty struct that only provides the `build` method.
pub struct Connection;

///
pub struct ConnectionBuilder<Tls>
where
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
    Tls::Stream: Sync + Send,
    Tls::TlsConnect: Sync + Send,
    <Tls::TlsConnect as TlsConnect<Socket>>::Future: Send,
{
    url: String,
    tls: Tls,
    recycling_method: RecyclingMethod,
    max_pool_size: Option<usize>,
    runtime: Option<Runtime>,
}

impl<Tls> ConnectionBuilder<Tls>
where
    Tls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
    Tls::Stream: Sync + Send,
    Tls::TlsConnect: Sync + Send,
    <Tls::TlsConnect as TlsConnect<Socket>>::Future: Send,
{
    fn to(url: impl Into<String>) -> ConnectionBuilder<NoTls> {
        ConnectionBuilder {
            url: url.into(),
            tls: NoTls,
            recycling_method: RecyclingMethod::Fast,
            max_pool_size: None,
            runtime: None,
        }
    }

    /// Set the Tls method.
    ///
    /// Use either [`postgres-openssl`](https://crates.io/crates/postgres-openssl)
    /// or [`postgres-nativ-tls`](https://crates.io/crates/postgres-native-tls)
    /// and their respective documentation.
    /// This function accepts the same types as `tokio-postgres`.
    pub fn tls<NewTls>(self, tls: NewTls) -> ConnectionBuilder<NewTls>
    where
        NewTls: MakeTlsConnect<Socket> + Clone + Send + Sync + 'static,
        NewTls::Stream: Sync + Send,
        NewTls::TlsConnect: Sync + Send,
        <NewTls::TlsConnect as TlsConnect<Socket>>::Future: Send,
    {
        ConnectionBuilder {
            tls,
            url: self.url,
            recycling_method: self.recycling_method,
            runtime: self.runtime,
            max_pool_size: self.max_pool_size,
        }
    }

    /// Set the maximum size of the connection pool,
    /// i.e. the maximum amount of concurrent connections to the database server.
    ///
    /// The default is `num_cpus * 4`, ignoring hyperthreading, etc.
    pub fn max_pool_size(mut self, n: usize) -> Self {
        self.max_pool_size = Some(n);

        self
    }

    /// Finish the setup and build the pool.
    ///
    /// Fails if
    ///  - the url couldn't be parsed, or
    ///  - some other configuration error has been made.
    pub fn connect(self) -> Result<(), crate::Error> {
        let config = Config::from_str(&self.url)?;
        let manager_config = ManagerConfig {
            recycling_method: self.recycling_method,
        };

        let manager = Manager::from_config(config, self.tls, manager_config);
        let mut builder = Pool::builder(manager).runtime(Runtime::Tokio1);

        if let Some(n) = self.max_pool_size {
            builder = builder.max_size(n);
        }

        let pool = builder.build()?;

        crate::set_pool(pool)?;

        Ok(())
    }
}

impl Connection {
    /// Start building a new connection (pool).
    ///
    /// This returns a [`ConnectionBuilder`] which can be configured
    /// using the builder pattern.
    ///
    /// If you are fine with the default configuration
    /// (`max_pool_size = num_cpus * 4` and no Tls) or have
    /// configured to your needs you  can finish the setup
    /// by calling `.connect()`.
    ///  
    /// A connection must be created before executing any
    /// queries or the like.
    /// Doing otherwise will result in a runime error.
    ///
    /// # Example
    /// ```ignore
    /// use pg_worm::prelude::*;
    ///
    /// Connection::build("postgres://postgres").connect()?;    
    /// ```
    ///
    pub fn build(connection_string: impl Into<String>) -> ConnectionBuilder<NoTls> {
        ConnectionBuilder::<NoTls>::to(connection_string)
    }
}