redis_oxide/commands/
list.rs

1//! Command builders for Redis List operations
2
3use crate::commands::Command;
4use crate::core::{
5    error::{RedisError, RedisResult},
6    value::RespValue,
7};
8use crate::pipeline::PipelineCommand;
9
10/// Represents the `LPUSH` command.
11#[derive(Debug, Clone)]
12pub struct LPushCommand {
13    key: String,
14    values: Vec<String>,
15}
16
17impl LPushCommand {
18    /// Create a new `LPUSH` command.
19    #[must_use]
20    pub fn new(key: impl Into<String>, values: Vec<String>) -> Self {
21        Self {
22            key: key.into(),
23            values,
24        }
25    }
26}
27
28impl Command for LPushCommand {
29    type Output = i64;
30
31    fn command_name(&self) -> &str {
32        "LPUSH"
33    }
34
35    fn args(&self) -> Vec<RespValue> {
36        let mut args = vec![RespValue::from(self.key.clone())];
37        for value in &self.values {
38            args.push(RespValue::from(value.clone()));
39        }
40        args
41    }
42
43    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
44        response.as_int()
45    }
46
47    fn keys(&self) -> Vec<&[u8]> {
48        vec![self.key.as_bytes()]
49    }
50}
51
52impl PipelineCommand for LPushCommand {
53    fn name(&self) -> &str {
54        self.command_name()
55    }
56
57    fn args(&self) -> Vec<RespValue> {
58        <Self as Command>::args(self)
59    }
60
61    fn key(&self) -> Option<String> {
62        Some(self.key.clone())
63    }
64}
65
66/// Represents the `RPUSH` command.
67#[derive(Debug, Clone)]
68pub struct RPushCommand {
69    key: String,
70    values: Vec<String>,
71}
72
73impl RPushCommand {
74    /// Create a new `RPUSH` command.
75    #[must_use]
76    pub fn new(key: impl Into<String>, values: Vec<String>) -> Self {
77        Self {
78            key: key.into(),
79            values,
80        }
81    }
82}
83
84impl Command for RPushCommand {
85    type Output = i64;
86
87    fn command_name(&self) -> &str {
88        "RPUSH"
89    }
90
91    fn args(&self) -> Vec<RespValue> {
92        let mut args = vec![RespValue::from(self.key.clone())];
93        for value in &self.values {
94            args.push(RespValue::from(value.clone()));
95        }
96        args
97    }
98
99    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
100        response.as_int()
101    }
102
103    fn keys(&self) -> Vec<&[u8]> {
104        vec![self.key.as_bytes()]
105    }
106}
107
108impl PipelineCommand for RPushCommand {
109    fn name(&self) -> &str {
110        self.command_name()
111    }
112
113    fn args(&self) -> Vec<RespValue> {
114        <Self as Command>::args(self)
115    }
116
117    fn key(&self) -> Option<String> {
118        Some(self.key.clone())
119    }
120}
121
122/// Represents the `LPOP` command.
123#[derive(Debug, Clone)]
124pub struct LPopCommand {
125    key: String,
126}
127
128impl LPopCommand {
129    /// Create a new `LPOP` command.
130    #[must_use]
131    pub fn new(key: impl Into<String>) -> Self {
132        Self { key: key.into() }
133    }
134}
135
136impl Command for LPopCommand {
137    type Output = Option<String>;
138
139    fn command_name(&self) -> &str {
140        "LPOP"
141    }
142
143    fn args(&self) -> Vec<RespValue> {
144        vec![RespValue::from(self.key.clone())]
145    }
146
147    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
148        match response {
149            RespValue::BulkString(b) => String::from_utf8(b.to_vec())
150                .map(Some)
151                .map_err(|e| RedisError::Type(format!("Invalid UTF-8: {e}"))),
152            RespValue::Null => Ok(None),
153            _ => Err(RedisError::Type(format!(
154                "Unexpected response type for LPOP: {:?}",
155                response
156            ))),
157        }
158    }
159
160    fn keys(&self) -> Vec<&[u8]> {
161        vec![self.key.as_bytes()]
162    }
163}
164
165impl PipelineCommand for LPopCommand {
166    fn name(&self) -> &str {
167        self.command_name()
168    }
169
170    fn args(&self) -> Vec<RespValue> {
171        <Self as Command>::args(self)
172    }
173
174    fn key(&self) -> Option<String> {
175        Some(self.key.clone())
176    }
177}
178
179/// Represents the `RPOP` command.
180#[derive(Debug, Clone)]
181pub struct RPopCommand {
182    key: String,
183}
184
185impl RPopCommand {
186    /// Create a new `RPOP` command.
187    #[must_use]
188    pub fn new(key: impl Into<String>) -> Self {
189        Self { key: key.into() }
190    }
191}
192
193impl Command for RPopCommand {
194    type Output = Option<String>;
195
196    fn command_name(&self) -> &str {
197        "RPOP"
198    }
199
200    fn args(&self) -> Vec<RespValue> {
201        vec![RespValue::from(self.key.clone())]
202    }
203
204    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
205        match response {
206            RespValue::BulkString(b) => String::from_utf8(b.to_vec())
207                .map(Some)
208                .map_err(|e| RedisError::Type(format!("Invalid UTF-8: {e}"))),
209            RespValue::Null => Ok(None),
210            _ => Err(RedisError::Type(format!(
211                "Unexpected response type for RPOP: {:?}",
212                response
213            ))),
214        }
215    }
216
217    fn keys(&self) -> Vec<&[u8]> {
218        vec![self.key.as_bytes()]
219    }
220}
221
222impl PipelineCommand for RPopCommand {
223    fn name(&self) -> &str {
224        self.command_name()
225    }
226
227    fn args(&self) -> Vec<RespValue> {
228        <Self as Command>::args(self)
229    }
230
231    fn key(&self) -> Option<String> {
232        Some(self.key.clone())
233    }
234}
235
236/// Represents the `LRANGE` command.
237#[derive(Debug, Clone)]
238pub struct LRangeCommand {
239    key: String,
240    start: i64,
241    stop: i64,
242}
243
244impl LRangeCommand {
245    /// Create a new `LRANGE` command.
246    #[must_use]
247    pub fn new(key: impl Into<String>, start: i64, stop: i64) -> Self {
248        Self {
249            key: key.into(),
250            start,
251            stop,
252        }
253    }
254}
255
256impl Command for LRangeCommand {
257    type Output = Vec<String>;
258
259    fn command_name(&self) -> &str {
260        "LRANGE"
261    }
262
263    fn args(&self) -> Vec<RespValue> {
264        vec![
265            RespValue::from(self.key.clone()),
266            RespValue::from(self.start),
267            RespValue::from(self.stop),
268        ]
269    }
270
271    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
272        match response {
273            RespValue::Array(items) => {
274                let mut result = Vec::new();
275                for item in items {
276                    match item {
277                        RespValue::BulkString(b) => {
278                            let s = String::from_utf8(b.to_vec())
279                                .map_err(|e| RedisError::Type(format!("Invalid UTF-8: {e}")))?;
280                            result.push(s);
281                        }
282                        RespValue::Null => {
283                            // Skip null values
284                        }
285                        _ => {
286                            return Err(RedisError::Type(format!(
287                                "Unexpected item type in LRANGE response: {:?}",
288                                item
289                            )))
290                        }
291                    }
292                }
293                Ok(result)
294            }
295            _ => Err(RedisError::Type(format!(
296                "Unexpected response type for LRANGE: {:?}",
297                response
298            ))),
299        }
300    }
301
302    fn keys(&self) -> Vec<&[u8]> {
303        vec![self.key.as_bytes()]
304    }
305}
306
307impl PipelineCommand for LRangeCommand {
308    fn name(&self) -> &str {
309        self.command_name()
310    }
311
312    fn args(&self) -> Vec<RespValue> {
313        <Self as Command>::args(self)
314    }
315
316    fn key(&self) -> Option<String> {
317        Some(self.key.clone())
318    }
319}
320
321/// Represents the `LLEN` command.
322#[derive(Debug, Clone)]
323pub struct LLenCommand {
324    key: String,
325}
326
327impl LLenCommand {
328    /// Create a new `LLEN` command.
329    #[must_use]
330    pub fn new(key: impl Into<String>) -> Self {
331        Self { key: key.into() }
332    }
333}
334
335impl Command for LLenCommand {
336    type Output = i64;
337
338    fn command_name(&self) -> &str {
339        "LLEN"
340    }
341
342    fn args(&self) -> Vec<RespValue> {
343        vec![RespValue::from(self.key.clone())]
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
355impl PipelineCommand for LLenCommand {
356    fn name(&self) -> &str {
357        self.command_name()
358    }
359
360    fn args(&self) -> Vec<RespValue> {
361        <Self as Command>::args(self)
362    }
363
364    fn key(&self) -> Option<String> {
365        Some(self.key.clone())
366    }
367}
368
369/// Represents the `LINDEX` command.
370#[derive(Debug, Clone)]
371pub struct LIndexCommand {
372    key: String,
373    index: i64,
374}
375
376impl LIndexCommand {
377    /// Create a new `LINDEX` command.
378    #[must_use]
379    pub fn new(key: impl Into<String>, index: i64) -> Self {
380        Self {
381            key: key.into(),
382            index,
383        }
384    }
385}
386
387impl Command for LIndexCommand {
388    type Output = Option<String>;
389
390    fn command_name(&self) -> &str {
391        "LINDEX"
392    }
393
394    fn args(&self) -> Vec<RespValue> {
395        vec![
396            RespValue::from(self.key.clone()),
397            RespValue::from(self.index),
398        ]
399    }
400
401    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
402        match response {
403            RespValue::BulkString(b) => String::from_utf8(b.to_vec())
404                .map(Some)
405                .map_err(|e| RedisError::Type(format!("Invalid UTF-8: {e}"))),
406            RespValue::Null => Ok(None),
407            _ => Err(RedisError::Type(format!(
408                "Unexpected response type for LINDEX: {:?}",
409                response
410            ))),
411        }
412    }
413
414    fn keys(&self) -> Vec<&[u8]> {
415        vec![self.key.as_bytes()]
416    }
417}
418
419impl PipelineCommand for LIndexCommand {
420    fn name(&self) -> &str {
421        self.command_name()
422    }
423
424    fn args(&self) -> Vec<RespValue> {
425        <Self as Command>::args(self)
426    }
427
428    fn key(&self) -> Option<String> {
429        Some(self.key.clone())
430    }
431}
432
433/// Represents the `LSET` command.
434#[derive(Debug, Clone)]
435pub struct LSetCommand {
436    key: String,
437    index: i64,
438    value: String,
439}
440
441impl LSetCommand {
442    /// Create a new `LSET` command.
443    #[must_use]
444    pub fn new(key: impl Into<String>, index: i64, value: impl Into<String>) -> Self {
445        Self {
446            key: key.into(),
447            index,
448            value: value.into(),
449        }
450    }
451}
452
453impl Command for LSetCommand {
454    type Output = String;
455
456    fn command_name(&self) -> &str {
457        "LSET"
458    }
459
460    fn args(&self) -> Vec<RespValue> {
461        vec![
462            RespValue::from(self.key.clone()),
463            RespValue::from(self.index),
464            RespValue::from(self.value.clone()),
465        ]
466    }
467
468    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
469        response.as_string()
470    }
471
472    fn keys(&self) -> Vec<&[u8]> {
473        vec![self.key.as_bytes()]
474    }
475}
476
477impl PipelineCommand for LSetCommand {
478    fn name(&self) -> &str {
479        self.command_name()
480    }
481
482    fn args(&self) -> Vec<RespValue> {
483        <Self as Command>::args(self)
484    }
485
486    fn key(&self) -> Option<String> {
487        Some(self.key.clone())
488    }
489}