redis_oxide/commands/
mod.rs1use crate::core::{error::RedisResult, value::RespValue};
6use std::time::Duration;
7
8pub trait Command {
10 type Output;
12
13 fn command_name(&self) -> &str;
15
16 fn args(&self) -> Vec<RespValue>;
18
19 fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output>;
21
22 fn keys(&self) -> Vec<&[u8]>;
24}
25
26pub struct GetCommand {
28 key: String,
29}
30
31impl GetCommand {
32 pub fn new(key: impl Into<String>) -> Self {
34 Self { key: key.into() }
35 }
36}
37
38impl Command for GetCommand {
39 type Output = Option<String>;
40
41 fn command_name(&self) -> &str {
42 "GET"
43 }
44
45 fn args(&self) -> Vec<RespValue> {
46 vec![RespValue::from(self.key.as_str())]
47 }
48
49 fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
50 if response.is_null() {
51 Ok(None)
52 } else {
53 Ok(Some(response.as_string()?))
54 }
55 }
56
57 fn keys(&self) -> Vec<&[u8]> {
58 vec![self.key.as_bytes()]
59 }
60}
61
62pub struct SetCommand {
64 key: String,
65 value: String,
66 expiration: Option<Duration>,
67 nx: bool,
68 xx: bool,
69}
70
71impl SetCommand {
72 pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
74 Self {
75 key: key.into(),
76 value: value.into(),
77 expiration: None,
78 nx: false,
79 xx: false,
80 }
81 }
82
83 pub fn expire(mut self, duration: Duration) -> Self {
85 self.expiration = Some(duration);
86 self
87 }
88
89 pub fn only_if_not_exists(mut self) -> Self {
91 self.nx = true;
92 self
93 }
94
95 pub fn only_if_exists(mut self) -> Self {
97 self.xx = true;
98 self
99 }
100}
101
102impl Command for SetCommand {
103 type Output = bool;
104
105 fn command_name(&self) -> &str {
106 "SET"
107 }
108
109 fn args(&self) -> Vec<RespValue> {
110 let mut args = vec![
111 RespValue::from(self.key.as_str()),
112 RespValue::from(self.value.as_str()),
113 ];
114
115 if let Some(duration) = self.expiration {
116 args.push(RespValue::from("EX"));
117 args.push(RespValue::from(duration.as_secs().to_string()));
118 }
119
120 if self.nx {
121 args.push(RespValue::from("NX"));
122 }
123
124 if self.xx {
125 args.push(RespValue::from("XX"));
126 }
127
128 args
129 }
130
131 fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
132 match response {
133 RespValue::SimpleString(ref s) if s == "OK" => Ok(true),
134 _ => Ok(false),
136 }
137 }
138
139 fn keys(&self) -> Vec<&[u8]> {
140 vec![self.key.as_bytes()]
141 }
142}
143
144pub struct DelCommand {
146 keys: Vec<String>,
147}
148
149impl DelCommand {
150 pub fn new(keys: Vec<String>) -> Self {
152 Self { keys }
153 }
154}
155
156impl Command for DelCommand {
157 type Output = i64;
158
159 fn command_name(&self) -> &str {
160 "DEL"
161 }
162
163 fn args(&self) -> Vec<RespValue> {
164 self.keys
165 .iter()
166 .map(|k| RespValue::from(k.as_str()))
167 .collect()
168 }
169
170 fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
171 response.as_int()
172 }
173
174 fn keys(&self) -> Vec<&[u8]> {
175 self.keys.iter().map(String::as_bytes).collect()
176 }
177}
178
179pub struct ExistsCommand {
181 keys: Vec<String>,
182}
183
184impl ExistsCommand {
185 pub fn new(keys: Vec<String>) -> Self {
187 Self { keys }
188 }
189}
190
191impl Command for ExistsCommand {
192 type Output = i64;
193
194 fn command_name(&self) -> &str {
195 "EXISTS"
196 }
197
198 fn args(&self) -> Vec<RespValue> {
199 self.keys
200 .iter()
201 .map(|k| RespValue::from(k.as_str()))
202 .collect()
203 }
204
205 fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
206 response.as_int()
207 }
208
209 fn keys(&self) -> Vec<&[u8]> {
210 self.keys.iter().map(String::as_bytes).collect()
211 }
212}
213
214pub struct ExpireCommand {
216 key: String,
217 seconds: i64,
218}
219
220impl ExpireCommand {
221 pub fn new(key: impl Into<String>, duration: Duration) -> Self {
223 #[allow(clippy::cast_possible_wrap)]
224 Self {
225 key: key.into(),
226 seconds: duration.as_secs() as i64,
227 }
228 }
229}
230
231impl Command for ExpireCommand {
232 type Output = bool;
233
234 fn command_name(&self) -> &str {
235 "EXPIRE"
236 }
237
238 fn args(&self) -> Vec<RespValue> {
239 vec![
240 RespValue::from(self.key.as_str()),
241 RespValue::from(self.seconds.to_string()),
242 ]
243 }
244
245 fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
246 Ok(response.as_int()? == 1)
247 }
248
249 fn keys(&self) -> Vec<&[u8]> {
250 vec![self.key.as_bytes()]
251 }
252}
253
254pub struct TtlCommand {
256 key: String,
257}
258
259impl TtlCommand {
260 pub fn new(key: impl Into<String>) -> Self {
262 Self { key: key.into() }
263 }
264}
265
266impl Command for TtlCommand {
267 type Output = Option<i64>;
268
269 fn command_name(&self) -> &str {
270 "TTL"
271 }
272
273 fn args(&self) -> Vec<RespValue> {
274 vec![RespValue::from(self.key.as_str())]
275 }
276
277 fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
278 let ttl = response.as_int()?;
279 if ttl < 0 {
280 Ok(None) } else {
282 Ok(Some(ttl))
283 }
284 }
285
286 fn keys(&self) -> Vec<&[u8]> {
287 vec![self.key.as_bytes()]
288 }
289}
290
291pub struct IncrCommand {
293 key: String,
294}
295
296impl IncrCommand {
297 pub fn new(key: impl Into<String>) -> Self {
299 Self { key: key.into() }
300 }
301}
302
303impl Command for IncrCommand {
304 type Output = i64;
305
306 fn command_name(&self) -> &str {
307 "INCR"
308 }
309
310 fn args(&self) -> Vec<RespValue> {
311 vec![RespValue::from(self.key.as_str())]
312 }
313
314 fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
315 response.as_int()
316 }
317
318 fn keys(&self) -> Vec<&[u8]> {
319 vec![self.key.as_bytes()]
320 }
321}
322
323pub struct DecrCommand {
325 key: String,
326}
327
328impl DecrCommand {
329 pub fn new(key: impl Into<String>) -> Self {
331 Self { key: key.into() }
332 }
333}
334
335impl Command for DecrCommand {
336 type Output = i64;
337
338 fn command_name(&self) -> &str {
339 "DECR"
340 }
341
342 fn args(&self) -> Vec<RespValue> {
343 vec![RespValue::from(self.key.as_str())]
344 }
345
346 fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
347 response.as_int()
348 }
349
350 fn keys(&self) -> Vec<&[u8]> {
351 vec![self.key.as_bytes()]
352 }
353}
354
355pub struct IncrByCommand {
357 key: String,
358 increment: i64,
359}
360
361impl IncrByCommand {
362 pub fn new(key: impl Into<String>, increment: i64) -> Self {
364 Self {
365 key: key.into(),
366 increment,
367 }
368 }
369}
370
371impl Command for IncrByCommand {
372 type Output = i64;
373
374 fn command_name(&self) -> &str {
375 "INCRBY"
376 }
377
378 fn args(&self) -> Vec<RespValue> {
379 vec![
380 RespValue::from(self.key.as_str()),
381 RespValue::from(self.increment.to_string()),
382 ]
383 }
384
385 fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
386 response.as_int()
387 }
388
389 fn keys(&self) -> Vec<&[u8]> {
390 vec![self.key.as_bytes()]
391 }
392}
393
394pub struct DecrByCommand {
396 key: String,
397 decrement: i64,
398}
399
400impl DecrByCommand {
401 pub fn new(key: impl Into<String>, decrement: i64) -> Self {
403 Self {
404 key: key.into(),
405 decrement,
406 }
407 }
408}
409
410impl Command for DecrByCommand {
411 type Output = i64;
412
413 fn command_name(&self) -> &str {
414 "DECRBY"
415 }
416
417 fn args(&self) -> Vec<RespValue> {
418 vec![
419 RespValue::from(self.key.as_str()),
420 RespValue::from(self.decrement.to_string()),
421 ]
422 }
423
424 fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
425 response.as_int()
426 }
427
428 fn keys(&self) -> Vec<&[u8]> {
429 vec![self.key.as_bytes()]
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 #[test]
438 fn test_get_command() {
439 let cmd = GetCommand::new("mykey");
440 assert_eq!(cmd.command_name(), "GET");
441 assert_eq!(cmd.keys(), vec![b"mykey"]);
442 }
443
444 #[test]
445 fn test_set_command_basic() {
446 let cmd = SetCommand::new("key", "value");
447 assert_eq!(cmd.command_name(), "SET");
448 let args = cmd.args();
449 assert_eq!(args.len(), 2);
450 }
451
452 #[test]
453 fn test_set_command_with_expiration() {
454 let cmd = SetCommand::new("key", "value").expire(Duration::from_secs(60));
455 let args = cmd.args();
456 assert_eq!(args.len(), 4); }
458
459 #[test]
460 fn test_set_command_nx() {
461 let cmd = SetCommand::new("key", "value").only_if_not_exists();
462 let args = cmd.args();
463 assert!(args.len() >= 3); }
465
466 #[test]
467 fn test_del_command() {
468 let cmd = DelCommand::new(vec!["key1".to_string(), "key2".to_string()]);
469 assert_eq!(cmd.command_name(), "DEL");
470 assert_eq!(cmd.keys().len(), 2);
471 }
472
473 #[test]
474 fn test_incr_command() {
475 let cmd = IncrCommand::new("counter");
476 assert_eq!(cmd.command_name(), "INCR");
477 assert_eq!(cmd.keys(), vec![b"counter"]);
478 }
479}