redis_oxide/commands/
sorted_set.rs

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