use core::fmt;
use std::convert::{TryFrom, TryInto};
use std::net::{SocketAddr, ToSocketAddrs};
use std::time::Duration;
use crate::ClientError;
#[derive(Clone, Debug)]
pub enum Host {
Tcp(TcpHost),
}
impl Host {
pub fn hostname(&self) -> Option<String> {
match self {
Host::Tcp(host) => Some(host.hostname.to_owned()),
}
}
}
impl Default for Host {
fn default() -> Self {
(String::from("localhost"), 3493)
.try_into()
.expect("Failed to parse local hostname; this is a bug.")
}
}
impl From<SocketAddr> for Host {
fn from(addr: SocketAddr) -> Self {
let hostname = addr.ip().to_string();
Self::Tcp(TcpHost { hostname, addr })
}
}
#[derive(Clone, Debug)]
pub struct TcpHost {
pub(crate) hostname: String,
pub(crate) addr: SocketAddr,
}
impl TryFrom<(String, u16)> for Host {
type Error = ClientError;
fn try_from(hostname_port: (String, u16)) -> Result<Self, Self::Error> {
let (hostname, _) = hostname_port.clone();
let addr = hostname_port
.to_socket_addrs()
.map_err(ClientError::Io)?
.next()
.ok_or_else(|| {
ClientError::Io(std::io::Error::new(
std::io::ErrorKind::AddrNotAvailable,
"no address given",
))
})?;
Ok(Host::Tcp(TcpHost { hostname, addr }))
}
}
#[derive(Clone)]
pub struct Auth {
pub(crate) username: String,
pub(crate) password: Option<String>,
}
impl Auth {
pub fn new(username: String, password: Option<String>) -> Self {
Auth { username, password }
}
}
impl fmt::Debug for Auth {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Auth")
.field("username", &self.username)
.field("password", &self.password.as_ref().map(|_| "(redacted)"))
.finish()
}
}
#[derive(Clone, Debug)]
pub struct Config {
pub(crate) host: Host,
pub(crate) auth: Option<Auth>,
pub(crate) timeout: Duration,
pub(crate) ssl: bool,
pub(crate) ssl_insecure: bool,
pub(crate) debug: bool,
}
impl Config {
pub fn new(
host: Host,
auth: Option<Auth>,
timeout: Duration,
ssl: bool,
ssl_insecure: bool,
debug: bool,
) -> Self {
Config {
host,
auth,
timeout,
ssl,
ssl_insecure,
debug,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct ConfigBuilder {
host: Option<Host>,
auth: Option<Auth>,
timeout: Option<Duration>,
ssl: Option<bool>,
ssl_insecure: Option<bool>,
debug: Option<bool>,
}
impl ConfigBuilder {
pub fn new() -> Self {
ConfigBuilder::default()
}
pub fn with_host(mut self, host: Host) -> Self {
self.host = Some(host);
self
}
pub fn with_auth(mut self, auth: Option<Auth>) -> Self {
self.auth = auth;
self
}
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
#[cfg(feature = "ssl")]
pub fn with_ssl(mut self, ssl: bool) -> Self {
self.ssl = Some(ssl);
self
}
#[cfg(feature = "ssl")]
pub fn with_insecure_ssl(mut self, ssl_insecure: bool) -> Self {
self.ssl_insecure = Some(ssl_insecure);
self
}
pub fn with_debug(mut self, debug: bool) -> Self {
self.debug = Some(debug);
self
}
pub fn build(self) -> Config {
Config::new(
self.host.unwrap_or_default(),
self.auth,
self.timeout.unwrap_or_else(|| Duration::from_secs(5)),
self.ssl.unwrap_or(false),
self.ssl_insecure.unwrap_or(false),
self.debug.unwrap_or(false),
)
}
}