use std::{fmt, path::PathBuf};
use redis::RedisError;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{CreatePoolError, Pool, PoolBuilder, PoolConfig, RedisResult, Runtime};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde"))]
pub struct Config {
pub url: Option<String>,
pub connection: Option<ConnectionInfo>,
pub pool: Option<PoolConfig>,
}
impl Config {
pub fn create_pool(&self, runtime: Option<Runtime>) -> Result<Pool, CreatePoolError> {
let mut builder = self.builder().map_err(CreatePoolError::Config)?;
if let Some(runtime) = runtime {
builder = builder.runtime(runtime);
}
builder.build().map_err(CreatePoolError::Build)
}
pub fn builder(&self) -> Result<PoolBuilder, ConfigError> {
let manager = match (&self.url, &self.connection) {
(Some(url), None) => crate::Manager::new(url.as_str())?,
(None, Some(connection)) => crate::Manager::new(connection.clone())?,
(None, None) => crate::Manager::new(ConnectionInfo::default())?,
(Some(_), Some(_)) => return Err(ConfigError::UrlAndConnectionSpecified),
};
let pool_config = self.get_pool_config();
Ok(Pool::builder(manager).config(pool_config))
}
#[must_use]
pub fn get_pool_config(&self) -> PoolConfig {
self.pool.unwrap_or_default()
}
#[must_use]
pub fn from_url<T: Into<String>>(url: T) -> Config {
Config {
url: Some(url.into()),
connection: None,
pool: None,
}
}
#[must_use]
pub fn from_connection_info<T: Into<ConnectionInfo>>(connection_info: T) -> Config {
Config {
url: None,
connection: Some(connection_info.into()),
pool: None,
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
url: None,
connection: Some(ConnectionInfo::default()),
pool: None,
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde"))]
pub enum ConnectionAddr {
Tcp(String, u16),
TcpTls {
host: String,
port: u16,
insecure: bool,
},
Unix(PathBuf),
}
impl Default for ConnectionAddr {
fn default() -> Self {
Self::Tcp("127.0.0.1".to_string(), 6379)
}
}
impl From<ConnectionAddr> for redis::ConnectionAddr {
fn from(addr: ConnectionAddr) -> Self {
match addr {
ConnectionAddr::Tcp(host, port) => Self::Tcp(host, port),
ConnectionAddr::TcpTls {
host,
port,
insecure,
} => Self::TcpTls {
host,
port,
insecure,
tls_params: None,
},
ConnectionAddr::Unix(path) => Self::Unix(path),
}
}
}
impl From<redis::ConnectionAddr> for ConnectionAddr {
fn from(addr: redis::ConnectionAddr) -> Self {
match addr {
redis::ConnectionAddr::Tcp(host, port) => Self::Tcp(host, port),
redis::ConnectionAddr::TcpTls {
host,
port,
insecure,
..
} => ConnectionAddr::TcpTls {
host,
port,
insecure,
},
redis::ConnectionAddr::Unix(path) => Self::Unix(path),
other => unimplemented!("unsupported redis::ConnectionAddr variant: {other:?}"),
}
}
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde"))]
pub struct ConnectionInfo {
pub addr: ConnectionAddr,
#[cfg_attr(feature = "serde", serde(flatten))]
pub redis: RedisConnectionInfo,
}
impl From<ConnectionInfo> for redis::ConnectionInfo {
fn from(info: ConnectionInfo) -> Self {
redis::IntoConnectionInfo::into_connection_info(redis::ConnectionAddr::from(info.addr))
.expect("converting ConnectionAddr into redis::ConnectionInfo is infallible")
.set_redis_settings(info.redis.into())
.set_tcp_settings(Default::default())
}
}
impl From<redis::ConnectionInfo> for ConnectionInfo {
fn from(info: redis::ConnectionInfo) -> Self {
Self {
addr: info.addr().clone().into(),
redis: info.redis_settings().clone().into(),
}
}
}
impl redis::IntoConnectionInfo for ConnectionInfo {
fn into_connection_info(self) -> RedisResult<redis::ConnectionInfo> {
Ok(self.into())
}
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde"))]
pub struct RedisConnectionInfo {
pub db: i64,
pub username: Option<String>,
pub password: Option<String>,
pub protocol: ProtocolVersion,
}
#[derive(Clone, Eq, PartialEq, Default, Debug, Copy)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde"))]
pub enum ProtocolVersion {
#[default]
RESP2,
RESP3,
}
impl From<RedisConnectionInfo> for redis::RedisConnectionInfo {
fn from(info: RedisConnectionInfo) -> Self {
let protocol = match info.protocol {
ProtocolVersion::RESP2 => redis::ProtocolVersion::RESP2,
ProtocolVersion::RESP3 => redis::ProtocolVersion::RESP3,
};
let mut result = redis::RedisConnectionInfo::default()
.set_db(info.db)
.set_protocol(protocol);
if let Some(username) = info.username {
result = result.set_username(username);
}
if let Some(password) = info.password {
result = result.set_password(password);
}
result
}
}
impl From<redis::RedisConnectionInfo> for RedisConnectionInfo {
fn from(info: redis::RedisConnectionInfo) -> Self {
let protocol = match info.protocol() {
redis::ProtocolVersion::RESP2 => ProtocolVersion::RESP2,
redis::ProtocolVersion::RESP3 => ProtocolVersion::RESP3,
other => unimplemented!("unsupported redis::ProtocolVersion variant: {other:?}"),
};
Self {
db: info.db(),
username: info.username().map(ToOwned::to_owned),
password: info.password().map(ToOwned::to_owned),
protocol,
}
}
}
#[derive(Debug)]
pub enum ConfigError {
UrlAndConnectionSpecified,
Redis(RedisError),
}
impl From<RedisError> for ConfigError {
fn from(e: RedisError) -> Self {
Self::Redis(e)
}
}
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UrlAndConnectionSpecified => write!(
f,
"url and connection must not be specified at the same time."
),
Self::Redis(e) => write!(f, "Redis: {}", e),
}
}
}
impl std::error::Error for ConfigError {}