1use super::Command;
6use crate::core::{error::RedisResult, value::RespValue};
7use crate::pipeline::PipelineCommand;
8use std::collections::HashMap;
9
10#[derive(Debug, Clone)]
12pub struct HGetCommand {
13    key: String,
14    field: String,
15}
16
17impl HGetCommand {
18    pub fn new(key: impl Into<String>, field: impl Into<String>) -> Self {
20        Self {
21            key: key.into(),
22            field: field.into(),
23        }
24    }
25}
26
27impl Command for HGetCommand {
28    type Output = Option<String>;
29
30    fn command_name(&self) -> &str {
31        "HGET"
32    }
33
34    fn args(&self) -> Vec<RespValue> {
35        vec![
36            RespValue::from(self.key.as_str()),
37            RespValue::from(self.field.as_str()),
38        ]
39    }
40
41    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
42        match response {
43            RespValue::Null => Ok(None),
44            RespValue::BulkString(bytes) => {
45                let s = String::from_utf8_lossy(&bytes).to_string();
46                Ok(Some(s))
47            }
48            _ => Err(crate::core::error::RedisError::Type(format!(
49                "Unexpected response type for HGET: {:?}",
50                response
51            ))),
52        }
53    }
54
55    fn keys(&self) -> Vec<&[u8]> {
56        vec![self.key.as_bytes()]
57    }
58}
59
60impl PipelineCommand for HGetCommand {
61    fn name(&self) -> &str {
62        self.command_name()
63    }
64
65    fn args(&self) -> Vec<RespValue> {
66        <Self as Command>::args(self)
67    }
68
69    fn key(&self) -> Option<String> {
70        Some(self.key.clone())
71    }
72}
73
74#[derive(Debug, Clone)]
76pub struct HSetCommand {
77    key: String,
78    field: String,
79    value: String,
80}
81
82impl HSetCommand {
83    pub fn new(key: impl Into<String>, field: impl Into<String>, value: impl Into<String>) -> Self {
85        Self {
86            key: key.into(),
87            field: field.into(),
88            value: value.into(),
89        }
90    }
91}
92
93impl Command for HSetCommand {
94    type Output = i64;
95
96    fn command_name(&self) -> &str {
97        "HSET"
98    }
99
100    fn args(&self) -> Vec<RespValue> {
101        vec![
102            RespValue::from(self.key.as_str()),
103            RespValue::from(self.field.as_str()),
104            RespValue::from(self.value.as_str()),
105        ]
106    }
107
108    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
109        response.as_int()
110    }
111
112    fn keys(&self) -> Vec<&[u8]> {
113        vec![self.key.as_bytes()]
114    }
115}
116
117impl PipelineCommand for HSetCommand {
118    fn name(&self) -> &str {
119        self.command_name()
120    }
121
122    fn args(&self) -> Vec<RespValue> {
123        <Self as Command>::args(self)
124    }
125
126    fn key(&self) -> Option<String> {
127        Some(self.key.clone())
128    }
129}
130
131#[derive(Debug, Clone)]
133pub struct HDelCommand {
134    key: String,
135    fields: Vec<String>,
136}
137
138impl HDelCommand {
139    pub fn new(key: impl Into<String>, fields: Vec<String>) -> Self {
141        Self {
142            key: key.into(),
143            fields,
144        }
145    }
146}
147
148impl Command for HDelCommand {
149    type Output = i64;
150
151    fn command_name(&self) -> &str {
152        "HDEL"
153    }
154
155    fn args(&self) -> Vec<RespValue> {
156        let mut args = vec![RespValue::from(self.key.as_str())];
157        for field in &self.fields {
158            args.push(RespValue::from(field.as_str()));
159        }
160        args
161    }
162
163    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
164        response.as_int()
165    }
166
167    fn keys(&self) -> Vec<&[u8]> {
168        vec![self.key.as_bytes()]
169    }
170}
171
172impl PipelineCommand for HDelCommand {
173    fn name(&self) -> &str {
174        self.command_name()
175    }
176
177    fn args(&self) -> Vec<RespValue> {
178        <Self as Command>::args(self)
179    }
180
181    fn key(&self) -> Option<String> {
182        Some(self.key.clone())
183    }
184}
185
186#[derive(Debug, Clone)]
188pub struct HGetAllCommand {
189    key: String,
190}
191
192impl HGetAllCommand {
193    pub fn new(key: impl Into<String>) -> Self {
195        Self { key: key.into() }
196    }
197}
198
199impl Command for HGetAllCommand {
200    type Output = HashMap<String, String>;
201
202    fn command_name(&self) -> &str {
203        "HGETALL"
204    }
205
206    fn args(&self) -> Vec<RespValue> {
207        vec![RespValue::from(self.key.as_str())]
208    }
209
210    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
211        match response {
212            RespValue::Array(items) => {
213                let mut result = HashMap::new();
214                let mut iter = items.into_iter();
215
216                while let (Some(field), Some(value)) = (iter.next(), iter.next()) {
217                    let field_str = field.as_string()?;
218                    let value_str = value.as_string()?;
219                    result.insert(field_str, value_str);
220                }
221
222                Ok(result)
223            }
224            _ => Err(crate::core::error::RedisError::Type(format!(
225                "Unexpected response type for HGETALL: {:?}",
226                response
227            ))),
228        }
229    }
230
231    fn keys(&self) -> Vec<&[u8]> {
232        vec![self.key.as_bytes()]
233    }
234}
235
236impl PipelineCommand for HGetAllCommand {
237    fn name(&self) -> &str {
238        self.command_name()
239    }
240
241    fn args(&self) -> Vec<RespValue> {
242        <Self as Command>::args(self)
243    }
244
245    fn key(&self) -> Option<String> {
246        Some(self.key.clone())
247    }
248}
249
250#[derive(Debug, Clone)]
252pub struct HMGetCommand {
253    key: String,
254    fields: Vec<String>,
255}
256
257impl HMGetCommand {
258    pub fn new(key: impl Into<String>, fields: Vec<String>) -> Self {
260        Self {
261            key: key.into(),
262            fields,
263        }
264    }
265}
266
267impl Command for HMGetCommand {
268    type Output = Vec<Option<String>>;
269
270    fn command_name(&self) -> &str {
271        "HMGET"
272    }
273
274    fn args(&self) -> Vec<RespValue> {
275        let mut args = vec![RespValue::from(self.key.as_str())];
276        for field in &self.fields {
277            args.push(RespValue::from(field.as_str()));
278        }
279        args
280    }
281
282    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
283        match response {
284            RespValue::Array(items) => {
285                let mut result = Vec::new();
286                for item in items {
287                    match item {
288                        RespValue::Null => result.push(None),
289                        RespValue::BulkString(bytes) => {
290                            let s = String::from_utf8_lossy(&bytes).to_string();
291                            result.push(Some(s));
292                        }
293                        _ => {
294                            return Err(crate::core::error::RedisError::Type(format!(
295                                "Unexpected item type in HMGET response: {:?}",
296                                item
297                            )))
298                        }
299                    }
300                }
301                Ok(result)
302            }
303            _ => Err(crate::core::error::RedisError::Type(format!(
304                "Unexpected response type for HMGET: {:?}",
305                response
306            ))),
307        }
308    }
309
310    fn keys(&self) -> Vec<&[u8]> {
311        vec![self.key.as_bytes()]
312    }
313}
314
315impl PipelineCommand for HMGetCommand {
316    fn name(&self) -> &str {
317        self.command_name()
318    }
319
320    fn args(&self) -> Vec<RespValue> {
321        <Self as Command>::args(self)
322    }
323
324    fn key(&self) -> Option<String> {
325        Some(self.key.clone())
326    }
327}
328
329#[derive(Debug, Clone)]
331pub struct HMSetCommand {
332    key: String,
333    fields: HashMap<String, String>,
334}
335
336impl HMSetCommand {
337    pub fn new(key: impl Into<String>, fields: HashMap<String, String>) -> Self {
339        Self {
340            key: key.into(),
341            fields,
342        }
343    }
344}
345
346impl Command for HMSetCommand {
347    type Output = String;
348
349    fn command_name(&self) -> &str {
350        "HMSET"
351    }
352
353    fn args(&self) -> Vec<RespValue> {
354        let mut args = vec![RespValue::from(self.key.as_str())];
355        for (field, value) in &self.fields {
356            args.push(RespValue::from(field.as_str()));
357            args.push(RespValue::from(value.as_str()));
358        }
359        args
360    }
361
362    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
363        response.as_string()
364    }
365
366    fn keys(&self) -> Vec<&[u8]> {
367        vec![self.key.as_bytes()]
368    }
369}
370
371impl PipelineCommand for HMSetCommand {
372    fn name(&self) -> &str {
373        self.command_name()
374    }
375
376    fn args(&self) -> Vec<RespValue> {
377        <Self as Command>::args(self)
378    }
379
380    fn key(&self) -> Option<String> {
381        Some(self.key.clone())
382    }
383}
384
385#[derive(Debug, Clone)]
387pub struct HLenCommand {
388    key: String,
389}
390
391impl HLenCommand {
392    pub fn new(key: impl Into<String>) -> Self {
394        Self { key: key.into() }
395    }
396}
397
398impl Command for HLenCommand {
399    type Output = i64;
400
401    fn command_name(&self) -> &str {
402        "HLEN"
403    }
404
405    fn args(&self) -> Vec<RespValue> {
406        vec![RespValue::from(self.key.as_str())]
407    }
408
409    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
410        response.as_int()
411    }
412
413    fn keys(&self) -> Vec<&[u8]> {
414        vec![self.key.as_bytes()]
415    }
416}
417
418impl PipelineCommand for HLenCommand {
419    fn name(&self) -> &str {
420        self.command_name()
421    }
422
423    fn args(&self) -> Vec<RespValue> {
424        <Self as Command>::args(self)
425    }
426
427    fn key(&self) -> Option<String> {
428        Some(self.key.clone())
429    }
430}
431
432#[derive(Debug, Clone)]
434pub struct HExistsCommand {
435    key: String,
436    field: String,
437}
438
439impl HExistsCommand {
440    pub fn new(key: impl Into<String>, field: impl Into<String>) -> Self {
442        Self {
443            key: key.into(),
444            field: field.into(),
445        }
446    }
447}
448
449impl Command for HExistsCommand {
450    type Output = bool;
451
452    fn command_name(&self) -> &str {
453        "HEXISTS"
454    }
455
456    fn args(&self) -> Vec<RespValue> {
457        vec![
458            RespValue::from(self.key.as_str()),
459            RespValue::from(self.field.as_str()),
460        ]
461    }
462
463    fn parse_response(&self, response: RespValue) -> RedisResult<Self::Output> {
464        match response {
465            RespValue::Integer(1) => Ok(true),
466            RespValue::Integer(0) => Ok(false),
467            _ => Err(crate::core::error::RedisError::Type(format!(
468                "Unexpected response type for HEXISTS: {:?}",
469                response
470            ))),
471        }
472    }
473
474    fn keys(&self) -> Vec<&[u8]> {
475        vec![self.key.as_bytes()]
476    }
477}
478
479impl PipelineCommand for HExistsCommand {
480    fn name(&self) -> &str {
481        self.command_name()
482    }
483
484    fn args(&self) -> Vec<RespValue> {
485        <Self as Command>::args(self)
486    }
487
488    fn key(&self) -> Option<String> {
489        Some(self.key.clone())
490    }
491}
492
493#[cfg(test)]
494mod tests {
495    use super::*;
496
497    #[test]
498    fn test_hget_command() {
499        let cmd = HGetCommand::new("myhash", "field1");
500        assert_eq!(cmd.command_name(), "HGET");
501        assert_eq!(cmd.keys(), vec![b"myhash"]);
502
503        let args = <HGetCommand as Command>::args(&cmd);
504        assert_eq!(args.len(), 2);
505    }
506
507    #[test]
508    fn test_hset_command() {
509        let cmd = HSetCommand::new("myhash", "field1", "value1");
510        assert_eq!(cmd.command_name(), "HSET");
511        assert_eq!(cmd.keys(), vec![b"myhash"]);
512
513        let args = <HSetCommand as Command>::args(&cmd);
514        assert_eq!(args.len(), 3);
515    }
516
517    #[test]
518    fn test_hdel_command() {
519        let cmd = HDelCommand::new("myhash", vec!["field1".to_string(), "field2".to_string()]);
520        assert_eq!(cmd.command_name(), "HDEL");
521        assert_eq!(cmd.keys(), vec![b"myhash"]);
522
523        let args = <HDelCommand as Command>::args(&cmd);
524        assert_eq!(args.len(), 3); }
526
527    #[test]
528    fn test_hgetall_command() {
529        let cmd = HGetAllCommand::new("myhash");
530        assert_eq!(cmd.command_name(), "HGETALL");
531        assert_eq!(cmd.keys(), vec![b"myhash"]);
532
533        let args = <HGetAllCommand as Command>::args(&cmd);
534        assert_eq!(args.len(), 1);
535    }
536
537    #[test]
538    fn test_hmget_command() {
539        let cmd = HMGetCommand::new("myhash", vec!["field1".to_string(), "field2".to_string()]);
540        assert_eq!(cmd.command_name(), "HMGET");
541        assert_eq!(cmd.keys(), vec![b"myhash"]);
542
543        let args = <HMGetCommand as Command>::args(&cmd);
544        assert_eq!(args.len(), 3); }
546
547    #[test]
548    fn test_hmset_command() {
549        let mut fields = HashMap::new();
550        fields.insert("field1".to_string(), "value1".to_string());
551        fields.insert("field2".to_string(), "value2".to_string());
552
553        let cmd = HMSetCommand::new("myhash", fields);
554        assert_eq!(cmd.command_name(), "HMSET");
555        assert_eq!(cmd.keys(), vec![b"myhash"]);
556
557        let args = <HMSetCommand as Command>::args(&cmd);
558        assert_eq!(args.len(), 5); }
560
561    #[test]
562    fn test_hlen_command() {
563        let cmd = HLenCommand::new("myhash");
564        assert_eq!(cmd.command_name(), "HLEN");
565        assert_eq!(cmd.keys(), vec![b"myhash"]);
566
567        let args = <HLenCommand as Command>::args(&cmd);
568        assert_eq!(args.len(), 1);
569    }
570
571    #[test]
572    fn test_hexists_command() {
573        let cmd = HExistsCommand::new("myhash", "field1");
574        assert_eq!(cmd.command_name(), "HEXISTS");
575        assert_eq!(cmd.keys(), vec![b"myhash"]);
576
577        let args = <HExistsCommand as Command>::args(&cmd);
578        assert_eq!(args.len(), 2);
579    }
580}