1use crate::error::RedisError;
2use crate::fake::FakeRedis;
3use crate::fake::FakeSubscriber;
4use redis::aio::ConnectionManager;
5use redis::Client;
6use sova_core::Request;
7use std::sync::{Arc, RwLock};
8
9#[derive(Default)]
10struct PoolInner {
11 conn: Option<ConnectionManager>,
12 url: Option<String>,
13 fake: Option<Arc<FakeRedis>>,
14}
15
16#[derive(Clone, Default)]
18pub struct RedisPool {
19 inner: Arc<RwLock<PoolInner>>,
20}
21
22impl RedisPool {
23 pub fn new() -> Self {
24 Self::default()
25 }
26
27 pub fn is_fake(&self) -> bool {
28 self.inner.read().unwrap().fake.is_some()
29 }
30
31 pub fn fake(&self) -> Option<Arc<FakeRedis>> {
32 self.inner.read().unwrap().fake.clone()
33 }
34
35 pub fn set_fake(&self, fake: FakeRedis) {
36 let mut g = self.inner.write().unwrap();
37 g.fake = Some(Arc::new(fake));
38 g.url = Some("fake://memory".into());
39 }
40
41 pub fn set_url(&self, url: impl Into<String>) {
43 self.inner.write().unwrap().url = Some(url.into());
44 }
45
46 pub fn url(&self) -> Result<String, RedisError> {
47 self.inner
48 .read()
49 .unwrap()
50 .url
51 .clone()
52 .ok_or_else(|| RedisError::msg("redis url not set"))
53 }
54
55 pub fn set(&self, conn: ConnectionManager) {
56 self.inner.write().unwrap().conn = Some(conn);
57 }
58
59 pub fn get(&self) -> Result<ConnectionManager, RedisError> {
60 let g = self.inner.read().unwrap();
61 if g.fake.is_some() {
62 return Err(RedisError::msg("fake redis: use FakeRedis API"));
63 }
64 g.conn
65 .clone()
66 .ok_or_else(|| RedisError::msg("redis not connected"))
67 }
68
69 pub fn clear(&self) {
70 let mut g = self.inner.write().unwrap();
71 g.conn.take();
72 }
73
74 pub async fn connect(url: &str) -> Result<ConnectionManager, RedisError> {
76 let client = Client::open(url).map_err(RedisError::from)?;
77 let mut conn = ConnectionManager::new(client)
78 .await
79 .map_err(RedisError::from)?;
80 let _: String = redis::cmd("PING")
81 .query_async(&mut conn)
82 .await
83 .map_err(RedisError::from)?;
84 Ok(conn)
85 }
86
87 pub async fn subscribe_fake(
88 &self,
89 channel: impl AsRef<str>,
90 ) -> Result<FakeSubscriber, RedisError> {
91 let fake = self
92 .fake()
93 .ok_or_else(|| RedisError::msg("fake redis not configured"))?;
94 Ok(fake.subscribe(channel.as_ref()))
95 }
96}
97
98pub trait RedisExt {
100 fn redis(&self) -> &RedisPool;
101}
102
103impl RedisExt for Request {
104 fn redis(&self) -> &RedisPool {
105 self.get::<RedisPool>()
106 .expect("Redis plugin is not installed (missing req.redis())")
107 }
108}