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}