use serde::Deserialize;
use std::time::Duration;
use crate::error::Error;
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Deserialize, Clone)]
pub struct RedisConnConfig {
#[serde(default = "RedisConnConfig::default_use_uds")]
pub use_uds: bool,
#[serde(default = "RedisConnConfig::default_socket")]
pub socket: String,
#[serde(default = "RedisConnConfig::default_host")]
pub host: String,
#[serde(default = "RedisConnConfig::default_port")]
pub port: u16,
#[serde(default = "RedisConnConfig::default_database")]
pub database: Option<u32>,
#[serde(default = "RedisConnConfig::default_username")]
pub username: Option<String>,
#[serde(default = "RedisConnConfig::default_password")]
pub password: Option<String>,
#[serde(default = "RedisConnConfig::default_pool_size")]
pub pool_size: usize,
#[serde(default = "RedisConnConfig::default_query_timeout")]
pub query_timeout: u32,
}
impl RedisConnConfig {
const fn default_use_uds() -> bool {
false
}
const fn default_socket() -> String {
String::new()
}
fn default_host() -> String {
"127.0.0.1".to_string()
}
const fn default_port() -> u16 {
6379
}
const fn default_username() -> Option<String> {
None
}
const fn default_password() -> Option<String> {
None
}
const fn default_database() -> Option<u32> {
None
}
const fn default_pool_size() -> usize {
4
}
const fn default_query_timeout() -> u32 {
5
}
}
impl Default for RedisConnConfig {
fn default() -> Self {
Self {
use_uds: Self::default_use_uds(),
socket: Self::default_socket(),
host: Self::default_host(),
port: Self::default_port(),
database: Self::default_database(),
username: Self::default_username(),
password: Self::default_password(),
pool_size: Self::default_pool_size(),
query_timeout: Self::default_query_timeout(),
}
}
}
impl RedisConnConfig {
#[must_use]
pub fn query_timeout(&self) -> Duration {
Duration::from_secs(u64::from(self.query_timeout))
}
#[must_use]
pub fn get_uri(&self) -> String {
let mut uri = String::new();
if self.use_uds {
uri.push_str(&format!("redis+unix://{}", self.socket));
if let Some(db) = self.database {
uri.push_str(&format!("?db={}", db));
}
if let Some(username) = &self.username {
uri.push_str(&format!("&username={}", username));
}
if let Some(password) = &self.password {
uri.push_str(&format!("&pass={}", password));
}
} else {
uri.push_str("redis://");
if let Some(username) = &self.username {
uri.push_str(username);
}
if let Some(password) = &self.password {
uri.push_str(&format!(":{}@", password));
}
uri.push_str(&format!("{}:{}", self.host, self.port));
if let Some(db) = self.database {
uri.push_str(&format!("/{}", db));
}
}
uri
}
}
#[derive(Clone)]
pub struct RedisConn {
client: redis::Client,
conn: Option<redis::aio::ConnectionManager>,
}
impl RedisConn {
pub fn new(config: &RedisConnConfig) -> Result<Self, Error> {
let client = redis::Client::open(config.get_uri())?;
Ok(Self { client, conn: None })
}
pub async fn init(&mut self) -> Result<(), Error> {
self.conn = Some(self.client.get_tokio_connection_manager().await?);
Ok(())
}
#[must_use]
pub fn get_conn(&self) -> Option<redis::aio::ConnectionManager> {
self.conn.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Error;
#[test]
fn test_redis_conn_config() {
let config: Result<RedisConnConfig, Error> = toml::from_str(
r#"
database = 1
username = "user1"
password = "password1"
pool_size = 8
query_timeout = 6
"#,
)
.map_err(Into::into);
assert!(config.is_ok());
let config = config.unwrap();
println!("{:?}", config.query_timeout);
assert_eq!(config.query_timeout(), Duration::from_secs(6));
let uri = config.get_uri();
assert_eq!(uri, "redis://user1:password1@127.0.0.1:6379/1");
}
#[test]
fn test_redis_conn_config_uds() {
let config: Result<RedisConnConfig, Error> = toml::from_str(
r#"
use_uds = true
socket = "/run/redis.sock"
database = 1
password = "password1"
pool_size = 8
"#,
)
.map_err(Into::into);
assert!(config.is_ok());
let config = config.unwrap();
let uri = config.get_uri();
assert_eq!(uri, "redis+unix:///run/redis.sock?db=1&pass=password1");
}
}