1use std::{fmt, path::PathBuf};
2
3use redis::RedisError;
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7use crate::{CreatePoolError, Pool, PoolBuilder, PoolConfig, RedisResult, Runtime};
8
9#[derive(Clone, Debug)]
37#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
38#[cfg_attr(feature = "serde", serde(crate = "serde"))]
39pub struct Config {
40 pub url: Option<String>,
44
45 pub connection: Option<ConnectionInfo>,
47
48 pub pool: Option<PoolConfig>,
50}
51
52impl Config {
53 pub fn create_pool(&self, runtime: Option<Runtime>) -> Result<Pool, CreatePoolError> {
59 let mut builder = self.builder().map_err(CreatePoolError::Config)?;
60 if let Some(runtime) = runtime {
61 builder = builder.runtime(runtime);
62 }
63 builder.build().map_err(CreatePoolError::Build)
64 }
65
66 pub fn builder(&self) -> Result<PoolBuilder, ConfigError> {
72 let manager = match (&self.url, &self.connection) {
73 (Some(url), None) => crate::Manager::new(url.as_str())?,
74 (None, Some(connection)) => crate::Manager::new(connection.clone())?,
75 (None, None) => crate::Manager::new(ConnectionInfo::default())?,
76 (Some(_), Some(_)) => return Err(ConfigError::UrlAndConnectionSpecified),
77 };
78 let pool_config = self.get_pool_config();
79 Ok(Pool::builder(manager).config(pool_config))
80 }
81
82 #[must_use]
85 pub fn get_pool_config(&self) -> PoolConfig {
86 self.pool.unwrap_or_default()
87 }
88
89 #[must_use]
92 pub fn from_url<T: Into<String>>(url: T) -> Config {
93 Config {
94 url: Some(url.into()),
95 connection: None,
96 pool: None,
97 }
98 }
99
100 #[must_use]
103 pub fn from_connection_info<T: Into<ConnectionInfo>>(connection_info: T) -> Config {
104 Config {
105 url: None,
106 connection: Some(connection_info.into()),
107 pool: None,
108 }
109 }
110}
111
112impl Default for Config {
113 fn default() -> Self {
114 Self {
115 url: None,
116 connection: Some(ConnectionInfo::default()),
117 pool: None,
118 }
119 }
120}
121
122#[derive(Clone, Debug)]
127#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
128#[cfg_attr(feature = "serde", serde(crate = "serde"))]
129pub enum ConnectionAddr {
130 Tcp(String, u16),
132
133 TcpTls {
135 host: String,
137
138 port: u16,
140
141 insecure: bool,
150 },
151
152 Unix(PathBuf),
154}
155
156impl Default for ConnectionAddr {
157 fn default() -> Self {
158 Self::Tcp("127.0.0.1".to_string(), 6379)
159 }
160}
161
162impl From<ConnectionAddr> for redis::ConnectionAddr {
163 fn from(addr: ConnectionAddr) -> Self {
164 match addr {
165 ConnectionAddr::Tcp(host, port) => Self::Tcp(host, port),
166 ConnectionAddr::TcpTls {
167 host,
168 port,
169 insecure,
170 } => Self::TcpTls {
171 host,
172 port,
173 insecure,
174 tls_params: None,
175 },
176 ConnectionAddr::Unix(path) => Self::Unix(path),
177 }
178 }
179}
180
181impl From<redis::ConnectionAddr> for ConnectionAddr {
182 fn from(addr: redis::ConnectionAddr) -> Self {
183 match addr {
184 redis::ConnectionAddr::Tcp(host, port) => Self::Tcp(host, port),
185 redis::ConnectionAddr::TcpTls {
186 host,
187 port,
188 insecure,
189 ..
190 } => ConnectionAddr::TcpTls {
191 host,
192 port,
193 insecure,
194 },
195 redis::ConnectionAddr::Unix(path) => Self::Unix(path),
196 other => unimplemented!("unsupported redis::ConnectionAddr variant: {other:?}"),
197 }
198 }
199}
200
201#[derive(Clone, Debug, Default)]
205#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
206#[cfg_attr(feature = "serde", serde(crate = "serde"))]
207pub struct ConnectionInfo {
208 pub addr: ConnectionAddr,
210
211 #[cfg_attr(feature = "serde", serde(flatten))]
213 pub redis: RedisConnectionInfo,
214}
215
216impl From<ConnectionInfo> for redis::ConnectionInfo {
217 fn from(info: ConnectionInfo) -> Self {
218 redis::IntoConnectionInfo::into_connection_info(redis::ConnectionAddr::from(info.addr))
219 .expect("converting ConnectionAddr into redis::ConnectionInfo is infallible")
220 .set_redis_settings(info.redis.into())
221 .set_tcp_settings(Default::default())
222 }
223}
224
225impl From<redis::ConnectionInfo> for ConnectionInfo {
226 fn from(info: redis::ConnectionInfo) -> Self {
227 Self {
228 addr: info.addr().clone().into(),
229 redis: info.redis_settings().clone().into(),
230 }
231 }
232}
233
234impl redis::IntoConnectionInfo for ConnectionInfo {
235 fn into_connection_info(self) -> RedisResult<redis::ConnectionInfo> {
236 Ok(self.into())
237 }
238}
239
240#[derive(Clone, Debug, Default)]
244#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
245#[cfg_attr(feature = "serde", serde(crate = "serde"))]
246pub struct RedisConnectionInfo {
247 pub db: i64,
249
250 pub username: Option<String>,
252
253 pub password: Option<String>,
255
256 pub protocol: ProtocolVersion,
258}
259
260#[derive(Clone, Eq, PartialEq, Default, Debug, Copy)]
264#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
265#[cfg_attr(feature = "serde", serde(crate = "serde"))]
266pub enum ProtocolVersion {
267 #[default]
269 RESP2,
270 RESP3,
272}
273
274impl From<RedisConnectionInfo> for redis::RedisConnectionInfo {
275 fn from(info: RedisConnectionInfo) -> Self {
276 let protocol = match info.protocol {
277 ProtocolVersion::RESP2 => redis::ProtocolVersion::RESP2,
278 ProtocolVersion::RESP3 => redis::ProtocolVersion::RESP3,
279 };
280 let mut result = redis::RedisConnectionInfo::default()
281 .set_db(info.db)
282 .set_protocol(protocol);
283 if let Some(username) = info.username {
284 result = result.set_username(username);
285 }
286 if let Some(password) = info.password {
287 result = result.set_password(password);
288 }
289 result
290 }
291}
292
293impl From<redis::RedisConnectionInfo> for RedisConnectionInfo {
294 fn from(info: redis::RedisConnectionInfo) -> Self {
295 let protocol = match info.protocol() {
296 redis::ProtocolVersion::RESP2 => ProtocolVersion::RESP2,
297 redis::ProtocolVersion::RESP3 => ProtocolVersion::RESP3,
298 other => unimplemented!("unsupported redis::ProtocolVersion variant: {other:?}"),
299 };
300 Self {
301 db: info.db(),
302 username: info.username().map(ToOwned::to_owned),
303 password: info.password().map(ToOwned::to_owned),
304 protocol,
305 }
306 }
307}
308
309#[derive(Debug)]
311pub enum ConfigError {
312 UrlAndConnectionSpecified,
314 Redis(RedisError),
316}
317
318impl From<RedisError> for ConfigError {
319 fn from(e: RedisError) -> Self {
320 Self::Redis(e)
321 }
322}
323
324impl fmt::Display for ConfigError {
325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326 match self {
327 Self::UrlAndConnectionSpecified => write!(
328 f,
329 "url and connection must not be specified at the same time."
330 ),
331 Self::Redis(e) => write!(f, "Redis: {}", e),
332 }
333 }
334}
335
336impl std::error::Error for ConfigError {}