|
1 | 1 | //! Connection parameters |
2 | 2 | use std::error::Error; |
3 | 3 | use std::path::PathBuf; |
| 4 | +use std::mem; |
4 | 5 |
|
5 | 6 | use params::url::Url; |
6 | 7 |
|
7 | 8 | mod url; |
8 | 9 |
|
9 | | -/// Specifies the target server to connect to. |
| 10 | +/// The host. |
10 | 11 | #[derive(Clone, Debug)] |
11 | | -pub enum ConnectTarget { |
12 | | - /// Connect via TCP to the specified host. |
| 12 | +pub enum Host { |
| 13 | + /// A TCP hostname. |
13 | 14 | Tcp(String), |
14 | | - /// Connect via a Unix domain socket in the specified directory. |
15 | | - /// |
16 | | - /// Unix sockets are only supported on Unixy platforms (i.e. not Windows). |
| 15 | + /// The path to a directory containing the server's Unix socket. |
17 | 16 | Unix(PathBuf), |
18 | 17 | } |
19 | 18 |
|
20 | 19 | /// Authentication information. |
21 | 20 | #[derive(Clone, Debug)] |
22 | | -pub struct UserInfo { |
| 21 | +pub struct User { |
| 22 | + name: String, |
| 23 | + password: Option<String>, |
| 24 | +} |
| 25 | + |
| 26 | +impl User { |
23 | 27 | /// The username. |
24 | | - pub user: String, |
| 28 | + pub fn name(&self) -> &str { |
| 29 | + &self.name |
| 30 | + } |
| 31 | + |
25 | 32 | /// An optional password. |
26 | | - pub password: Option<String>, |
| 33 | + pub fn password(&self) -> Option<&str> { |
| 34 | + self.password.as_ref().map(|p| &**p) |
| 35 | + } |
27 | 36 | } |
28 | 37 |
|
29 | 38 | /// Information necessary to open a new connection to a Postgres server. |
30 | 39 | #[derive(Clone, Debug)] |
31 | 40 | pub struct ConnectParams { |
32 | | - /// The target server. |
33 | | - pub target: ConnectTarget, |
| 41 | + host: Host, |
| 42 | + port: u16, |
| 43 | + user: Option<User>, |
| 44 | + database: Option<String>, |
| 45 | + options: Vec<(String, String)>, |
| 46 | +} |
| 47 | + |
| 48 | +impl ConnectParams { |
| 49 | + /// Returns a new builder. |
| 50 | + pub fn builder() -> Builder { |
| 51 | + Builder::new() |
| 52 | + } |
| 53 | + |
| 54 | + /// The target host. |
| 55 | + pub fn host(&self) -> &Host { |
| 56 | + &self.host |
| 57 | + } |
| 58 | + |
34 | 59 | /// The target port. |
35 | 60 | /// |
36 | | - /// Defaults to 5432 if not specified. |
37 | | - pub port: Option<u16>, |
38 | | - /// The user to login as. |
| 61 | + /// Defaults to 5432. |
| 62 | + pub fn port(&self) -> u16 { |
| 63 | + self.port |
| 64 | + } |
| 65 | + |
| 66 | + /// The user to log in as. |
39 | 67 | /// |
40 | | - /// `Connection::connect` requires a user but `cancel_query` does not. |
41 | | - pub user: Option<UserInfo>, |
| 68 | + /// A user is required to open a new connection but not to cancel a query. |
| 69 | + pub fn user(&self) -> Option<&User> { |
| 70 | + self.user.as_ref() |
| 71 | + } |
| 72 | + |
42 | 73 | /// The database to connect to. |
43 | | - /// |
44 | | - /// Defaults the value of `user`. |
45 | | - pub database: Option<String>, |
| 74 | + pub fn database(&self) -> Option<&str> { |
| 75 | + self.database.as_ref().map(|d| &**d) |
| 76 | + } |
| 77 | + |
46 | 78 | /// Runtime parameters to be passed to the Postgres backend. |
47 | | - pub options: Vec<(String, String)>, |
| 79 | + pub fn options(&self) -> &[(String, String)] { |
| 80 | + &self.options |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +/// A builder for `ConnectParams`. |
| 85 | +pub struct Builder { |
| 86 | + port: u16, |
| 87 | + user: Option<User>, |
| 88 | + database: Option<String>, |
| 89 | + options: Vec<(String, String)>, |
| 90 | +} |
| 91 | + |
| 92 | +impl Builder { |
| 93 | + /// Creates a new builder. |
| 94 | + pub fn new() -> Builder { |
| 95 | + Builder { |
| 96 | + port: 5432, |
| 97 | + user: None, |
| 98 | + database: None, |
| 99 | + options: vec![], |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + /// Sets the port. |
| 104 | + pub fn port(&mut self, port: u16) -> &mut Builder { |
| 105 | + self.port = port; |
| 106 | + self |
| 107 | + } |
| 108 | + |
| 109 | + /// Sets the user. |
| 110 | + pub fn user(&mut self, name: &str, password: Option<&str>) -> &mut Builder { |
| 111 | + self.user = Some(User { |
| 112 | + name: name.to_string(), |
| 113 | + password: password.map(ToString::to_string), |
| 114 | + }); |
| 115 | + self |
| 116 | + } |
| 117 | + |
| 118 | + /// Sets the database. |
| 119 | + pub fn database(&mut self, database: &str) -> &mut Builder { |
| 120 | + self.database = Some(database.to_string()); |
| 121 | + self |
| 122 | + } |
| 123 | + |
| 124 | + /// Adds a runtime parameter. |
| 125 | + pub fn option(&mut self, name: &str, value: &str) -> &mut Builder { |
| 126 | + self.options.push((name.to_string(), value.to_string())); |
| 127 | + self |
| 128 | + } |
| 129 | + |
| 130 | + /// Constructs a `ConnectParams` from the builder. |
| 131 | + pub fn build(&mut self, host: Host) -> ConnectParams { |
| 132 | + ConnectParams { |
| 133 | + host: host, |
| 134 | + port: self.port, |
| 135 | + user: self.user.take(), |
| 136 | + database: self.database.take(), |
| 137 | + options: mem::replace(&mut self.options, vec![]), |
| 138 | + } |
| 139 | + } |
48 | 140 | } |
49 | 141 |
|
50 | 142 | /// A trait implemented by types that can be converted into a `ConnectParams`. |
@@ -78,35 +170,33 @@ impl IntoConnectParams for Url { |
78 | 170 | fn into_connect_params(self) -> Result<ConnectParams, Box<Error + Sync + Send>> { |
79 | 171 | let Url { host, port, user, path: url::Path { mut path, query: options, .. }, .. } = self; |
80 | 172 |
|
81 | | - let maybe_path = url::decode_component(&host)?; |
82 | | - let target = if maybe_path.starts_with('/') { |
83 | | - ConnectTarget::Unix(PathBuf::from(maybe_path)) |
84 | | - } else { |
85 | | - ConnectTarget::Tcp(host) |
86 | | - }; |
| 173 | + let mut builder = ConnectParams::builder(); |
87 | 174 |
|
88 | | - let user = user.map(|url::UserInfo { user, pass }| { |
89 | | - UserInfo { |
90 | | - user: user, |
91 | | - password: pass, |
92 | | - } |
93 | | - }); |
| 175 | + if let Some(port) = port { |
| 176 | + builder.port(port); |
| 177 | + } |
94 | 178 |
|
95 | | - let database = if path.is_empty() { |
96 | | - None |
97 | | - } else { |
| 179 | + if let Some(info) = user { |
| 180 | + builder.user(&info.user, info.pass.as_ref().map(|p| &**p)); |
| 181 | + } |
| 182 | + |
| 183 | + if !path.is_empty() { |
98 | 184 | // path contains the leading / |
99 | | - path.remove(0); |
100 | | - Some(path) |
| 185 | + builder.database(&path[1..]); |
| 186 | + } |
| 187 | + |
| 188 | + for (name, value) in options { |
| 189 | + builder.option(&name, &value); |
| 190 | + } |
| 191 | + |
| 192 | + let maybe_path = url::decode_component(&host)?; |
| 193 | + let host = if maybe_path.starts_with('/') { |
| 194 | + Host::Unix(maybe_path.into()) |
| 195 | + } else { |
| 196 | + Host::Tcp(maybe_path) |
101 | 197 | }; |
102 | 198 |
|
103 | | - Ok(ConnectParams { |
104 | | - target: target, |
105 | | - port: port, |
106 | | - user: user, |
107 | | - database: database, |
108 | | - options: options, |
109 | | - }) |
| 199 | + Ok(builder.build(host)) |
110 | 200 | } |
111 | 201 | } |
112 | 202 |
|
0 commit comments