1use core::fmt;
2use std::convert::{TryFrom, TryInto};
3use std::net::{SocketAddr, ToSocketAddrs};
4use std::time::Duration;
5
6use crate::ClientError;
7
8#[derive(Clone, Debug)]
10pub enum Host {
11 Tcp(TcpHost),
13 }
15
16impl Host {
17 pub fn hostname(&self) -> Option<String> {
19 match self {
20 Host::Tcp(host) => Some(host.hostname.to_owned()),
21 }
23 }
24}
25
26impl Default for Host {
27 fn default() -> Self {
28 (String::from("localhost"), 3493)
29 .try_into()
30 .expect("Failed to parse local hostname; this is a bug.")
31 }
32}
33
34impl From<SocketAddr> for Host {
35 fn from(addr: SocketAddr) -> Self {
36 let hostname = addr.ip().to_string();
37 Self::Tcp(TcpHost { hostname, addr })
38 }
39}
40
41#[derive(Clone, Debug)]
43pub struct TcpHost {
44 pub(crate) hostname: String,
45 pub(crate) addr: SocketAddr,
46}
47
48impl TryFrom<(String, u16)> for Host {
49 type Error = ClientError;
50
51 fn try_from(hostname_port: (String, u16)) -> Result<Self, Self::Error> {
52 let (hostname, _) = hostname_port.clone();
53 let addr = hostname_port
54 .to_socket_addrs()
55 .map_err(ClientError::Io)?
56 .next()
57 .ok_or_else(|| {
58 ClientError::Io(std::io::Error::new(
59 std::io::ErrorKind::AddrNotAvailable,
60 "no address given",
61 ))
62 })?;
63 Ok(Host::Tcp(TcpHost { hostname, addr }))
64 }
65}
66
67#[derive(Clone)]
69pub struct Auth {
70 pub(crate) username: String,
72 pub(crate) password: Option<String>,
74}
75
76impl Auth {
77 pub fn new(username: String, password: Option<String>) -> Self {
79 Auth { username, password }
80 }
81}
82
83impl fmt::Debug for Auth {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 f.debug_struct("Auth")
86 .field("username", &self.username)
87 .field("password", &self.password.as_ref().map(|_| "(redacted)"))
88 .finish()
89 }
90}
91
92#[derive(Clone, Debug)]
94pub struct Config {
95 pub(crate) host: Host,
96 pub(crate) auth: Option<Auth>,
97 pub(crate) timeout: Duration,
98 pub(crate) ssl: bool,
99 pub(crate) ssl_insecure: bool,
100 pub(crate) debug: bool,
101}
102
103impl Config {
104 pub fn new(
106 host: Host,
107 auth: Option<Auth>,
108 timeout: Duration,
109 ssl: bool,
110 ssl_insecure: bool,
111 debug: bool,
112 ) -> Self {
113 Config {
114 host,
115 auth,
116 timeout,
117 ssl,
118 ssl_insecure,
119 debug,
120 }
121 }
122}
123
124#[derive(Clone, Debug, Default)]
126pub struct ConfigBuilder {
127 host: Option<Host>,
128 auth: Option<Auth>,
129 timeout: Option<Duration>,
130 ssl: Option<bool>,
131 ssl_insecure: Option<bool>,
132 debug: Option<bool>,
133}
134
135impl ConfigBuilder {
136 pub fn new() -> Self {
138 ConfigBuilder::default()
139 }
140
141 pub fn with_host(mut self, host: Host) -> Self {
143 self.host = Some(host);
144 self
145 }
146
147 pub fn with_auth(mut self, auth: Option<Auth>) -> Self {
149 self.auth = auth;
150 self
151 }
152
153 pub fn with_timeout(mut self, timeout: Duration) -> Self {
156 self.timeout = Some(timeout);
157 self
158 }
159
160 #[cfg(feature = "ssl")]
165 pub fn with_ssl(mut self, ssl: bool) -> Self {
166 self.ssl = Some(ssl);
167 self
168 }
169
170 #[cfg(feature = "ssl")]
174 pub fn with_insecure_ssl(mut self, ssl_insecure: bool) -> Self {
175 self.ssl_insecure = Some(ssl_insecure);
176 self
177 }
178
179 pub fn with_debug(mut self, debug: bool) -> Self {
181 self.debug = Some(debug);
182 self
183 }
184
185 pub fn build(self) -> Config {
187 Config::new(
188 self.host.unwrap_or_default(),
189 self.auth,
190 self.timeout.unwrap_or_else(|| Duration::from_secs(5)),
191 self.ssl.unwrap_or(false),
192 self.ssl_insecure.unwrap_or(false),
193 self.debug.unwrap_or(false),
194 )
195 }
196}