1use crate::error::RedisError;
4use crate::messaging::RedisMessage;
5use std::collections::HashMap;
6use std::sync::{Arc, Mutex};
7use std::time::{Duration, Instant};
8use tokio::sync::broadcast;
9
10const MAX_DB: u8 = 15;
11
12#[derive(Clone)]
13struct Entry {
14 value: String,
15 expires: Option<Instant>,
16}
17
18#[derive(Default)]
19struct Db {
20 keys: HashMap<String, Entry>,
21}
22
23struct Inner {
24 dbs: [Db; 16],
25 channels: HashMap<String, broadcast::Sender<Vec<u8>>>,
26}
27
28impl Default for Inner {
29 fn default() -> Self {
30 Self {
31 dbs: std::array::from_fn(|_| Db::default()),
32 channels: HashMap::new(),
33 }
34 }
35}
36
37#[derive(Clone, Default)]
39pub struct FakeRedis {
40 inner: Arc<Mutex<Inner>>,
41}
42
43impl FakeRedis {
44 pub fn new() -> Self {
45 Self::default()
46 }
47
48 fn purge_expired(db: &mut Db) {
49 let now = Instant::now();
50 db.keys.retain(|_, e| e.expires.is_none_or(|t| t > now));
51 }
52
53 fn db(inner: &mut Inner, db: u8) -> &mut Db {
54 &mut inner.dbs[db.min(MAX_DB) as usize]
55 }
56
57 pub fn select(&self, db: u8) -> Result<(), RedisError> {
58 let _ = db.min(MAX_DB);
59 Ok(())
60 }
61
62 pub fn get(&self, db: u8, key: &str) -> Result<Option<String>, RedisError> {
63 let mut g = self.inner.lock().unwrap();
64 let db = Self::db(&mut g, db);
65 Self::purge_expired(db);
66 Ok(db.keys.get(key).map(|e| e.value.clone()))
67 }
68
69 pub fn set(&self, db: u8, key: &str, value: &str) -> Result<(), RedisError> {
70 let mut g = self.inner.lock().unwrap();
71 let db = Self::db(&mut g, db);
72 db.keys.insert(
73 key.to_string(),
74 Entry {
75 value: value.to_string(),
76 expires: None,
77 },
78 );
79 Ok(())
80 }
81
82 pub fn setex(&self, db: u8, key: &str, ttl_secs: u64, value: &str) -> Result<(), RedisError> {
83 let mut g = self.inner.lock().unwrap();
84 let db = Self::db(&mut g, db);
85 db.keys.insert(
86 key.to_string(),
87 Entry {
88 value: value.to_string(),
89 expires: Some(Instant::now() + Duration::from_secs(ttl_secs)),
90 },
91 );
92 Ok(())
93 }
94
95 pub fn del(&self, db: u8, key: &str) -> Result<i64, RedisError> {
96 let mut g = self.inner.lock().unwrap();
97 let db = Self::db(&mut g, db);
98 Ok(if db.keys.remove(key).is_some() { 1 } else { 0 })
99 }
100
101 pub fn ttl(&self, db: u8, key: &str) -> Result<i64, RedisError> {
102 let mut g = self.inner.lock().unwrap();
103 let db = Self::db(&mut g, db);
104 Self::purge_expired(db);
105 match db.keys.get(key) {
106 None => Ok(-2),
107 Some(e) => match e.expires {
108 None => Ok(-1),
109 Some(t) => {
110 let left = t.saturating_duration_since(Instant::now());
111 Ok(left.as_secs() as i64)
112 }
113 },
114 }
115 }
116
117 pub fn key_type(&self, db: u8, key: &str) -> Result<String, RedisError> {
118 let mut g = self.inner.lock().unwrap();
119 let db = Self::db(&mut g, db);
120 Self::purge_expired(db);
121 Ok(if db.keys.contains_key(key) {
122 "string".into()
123 } else {
124 "none".into()
125 })
126 }
127
128 pub fn scan(
129 &self,
130 db: u8,
131 cursor: u64,
132 pattern: &str,
133 count: usize,
134 ) -> Result<(u64, Vec<String>), RedisError> {
135 let mut g = self.inner.lock().unwrap();
136 let db = Self::db(&mut g, db);
137 Self::purge_expired(db);
138 let mut keys: Vec<String> = db
139 .keys
140 .keys()
141 .filter(|k| glob_match(pattern, k))
142 .cloned()
143 .collect();
144 keys.sort();
145 let start = cursor as usize;
146 let end = (start + count).min(keys.len());
147 let slice = keys[start..end].to_vec();
148 let next = if end >= keys.len() { 0 } else { end as u64 };
149 Ok((next, slice))
150 }
151
152 pub fn publish(&self, channel: &str, message: &[u8]) -> Result<i64, RedisError> {
153 let mut g = self.inner.lock().unwrap();
154 let tx = g
155 .channels
156 .entry(channel.to_string())
157 .or_insert_with(|| broadcast::channel(64).0);
158 Ok(tx.send(message.to_vec()).unwrap_or(0) as i64)
159 }
160
161 pub fn subscribe(&self, channel: &str) -> FakeSubscriber {
162 let mut g = self.inner.lock().unwrap();
163 let tx = g
164 .channels
165 .entry(channel.to_string())
166 .or_insert_with(|| broadcast::channel(64).0);
167 FakeSubscriber {
168 rx: tx.subscribe(),
169 channel: channel.to_string(),
170 }
171 }
172}
173
174pub struct FakeSubscriber {
175 rx: broadcast::Receiver<Vec<u8>>,
176 channel: String,
177}
178
179impl FakeSubscriber {
180 pub async fn next(&mut self) -> Option<RedisMessage> {
181 loop {
182 match self.rx.recv().await {
183 Ok(payload) => {
184 return Some(RedisMessage {
185 channel: self.channel.clone(),
186 payload,
187 pattern: None,
188 });
189 }
190 Err(broadcast::error::RecvError::Lagged(_)) => continue,
191 Err(broadcast::error::RecvError::Closed) => return None,
192 }
193 }
194 }
195}
196
197fn glob_match(pattern: &str, key: &str) -> bool {
198 if pattern == "*" {
199 return true;
200 }
201 if let Some(prefix) = pattern.strip_suffix('*') {
202 return key.starts_with(prefix);
203 }
204 pattern == key
205}