1use super::client::ClientOperations;
2use super::list::ListOperations;
3use super::Command;
4use crate::client_registry::ClientRegistry;
5use crate::config::Config;
6use crate::protocol::resp::RespValue;
7use bytes::Bytes;
8use feoxdb::FeoxStore;
9use std::sync::Arc;
10
11fn match_pattern(key: &[u8], pattern: &str) -> bool {
13 let key_str = String::from_utf8_lossy(key);
14 glob_match(pattern, &key_str)
15}
16
17fn glob_match(pattern: &str, text: &str) -> bool {
19 let mut p_idx = 0;
20 let mut t_idx = 0;
21 let mut star_idx = None;
22 let mut star_match = None;
23
24 let pattern_bytes = pattern.as_bytes();
25 let text_bytes = text.as_bytes();
26
27 while t_idx < text_bytes.len() {
28 if p_idx < pattern_bytes.len() {
29 match pattern_bytes[p_idx] {
30 b'*' => {
31 star_idx = Some(p_idx);
32 star_match = Some(t_idx);
33 p_idx += 1;
34 }
35 b'?' => {
36 p_idx += 1;
37 t_idx += 1;
38 }
39 _ => {
40 if pattern_bytes[p_idx] == text_bytes[t_idx] {
41 p_idx += 1;
42 t_idx += 1;
43 } else if let Some(star) = star_idx {
44 p_idx = star + 1;
45 star_match = Some(star_match.unwrap() + 1);
46 t_idx = star_match.unwrap();
47 } else {
48 return false;
49 }
50 }
51 }
52 } else if let Some(star) = star_idx {
53 p_idx = star + 1;
54 star_match = Some(star_match.unwrap() + 1);
55 t_idx = star_match.unwrap();
56 } else {
57 return false;
58 }
59 }
60
61 while p_idx < pattern_bytes.len() && pattern_bytes[p_idx] == b'*' {
63 p_idx += 1;
64 }
65
66 p_idx == pattern_bytes.len()
67}
68
69fn extract_prefix(pattern: &str) -> &str {
71 for (i, ch) in pattern.char_indices() {
72 if ch == '*' || ch == '?' || ch == '[' {
73 return &pattern[..i];
74 }
75 }
76 pattern
77}
78
79#[derive(Clone)]
83pub struct CommandExecutor {
84 store: Arc<FeoxStore>,
85 list_ops: ListOperations,
86 client_ops: ClientOperations,
87 config: Config, start_time: std::time::Instant,
89 commands_processed: Arc<std::sync::atomic::AtomicU64>,
90 connection_id: Option<usize>,
91}
92
93impl CommandExecutor {
94 pub fn new(store: Arc<FeoxStore>, config: &Config) -> Self {
96 let list_ops = ListOperations::new(Arc::clone(&store));
97 Self {
98 store,
99 list_ops,
100 client_ops: ClientOperations::new(),
101 config: config.clone(),
102 start_time: std::time::Instant::now(),
103 commands_processed: Arc::new(std::sync::atomic::AtomicU64::new(0)),
104 connection_id: None,
105 }
106 }
107
108 pub fn with_client_info(mut self, registry: Arc<ClientRegistry>, connection_id: usize) -> Self {
110 self.client_ops = ClientOperations::with_registry(registry);
111 self.connection_id = Some(connection_id);
112 self
113 }
114
115 pub fn check_auth(&self, password: &str) -> bool {
117 self.config.check_password(password)
118 }
119
120 #[inline(always)]
122 pub fn fast_set(&self, key: &[u8], value: &[u8]) -> Result<(), feoxdb::FeoxError> {
123 self.store.insert_with_timestamp(key, value, None)
124 }
125
126 #[inline(always)]
128 pub fn fast_set_bytes(&self, key: &[u8], value: bytes::Bytes) -> Result<(), feoxdb::FeoxError> {
129 self.store.insert_bytes_with_timestamp(key, value, None)
130 }
131
132 #[inline(always)]
134 pub fn fast_get(&self, key: &[u8]) -> Result<bytes::Bytes, feoxdb::FeoxError> {
135 self.store.get_bytes(key)
136 }
137
138 #[inline]
140 pub fn execute(&self, cmd: Command) -> RespValue {
141 use std::sync::atomic::Ordering;
142
143 self.commands_processed.fetch_add(1, Ordering::Relaxed);
145
146 match cmd {
147 Command::Get(key) => match self.store.get_bytes(&key) {
148 Ok(value) => RespValue::BulkString(Some(value)),
149 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::BulkString(None),
150 Err(e) => RespValue::Error(format!("ERR {}", e)),
151 },
152
153 Command::Set { key, value, ex, px } => {
154 let result = if let Some(seconds) = ex {
155 self.store
156 .insert_bytes_with_ttl_and_timestamp(&key, value, seconds, None)
157 } else if let Some(millis) = px {
158 self.store
159 .insert_bytes_with_ttl_and_timestamp(&key, value, millis / 1000, None)
160 } else {
161 self.store.insert_bytes_with_timestamp(&key, value, None)
162 };
163
164 match result {
165 Ok(_) => RespValue::SimpleString(Bytes::from_static(b"OK")),
166 Err(e) => RespValue::Error(format!("ERR {}", e)),
167 }
168 }
169
170 Command::Del(keys) => {
171 let mut count = 0i64;
172 for key in keys {
173 if self.store.delete(&key).is_ok() {
174 count += 1;
175 }
176 }
177 RespValue::Integer(count)
178 }
179
180 Command::Exists(keys) => {
181 let count = keys
182 .iter()
183 .filter(|key| self.store.contains_key(key))
184 .count() as i64;
185 RespValue::Integer(count)
186 }
187
188 Command::Incr(key) => match self.store.atomic_increment(&key, 1) {
189 Ok(val) => RespValue::Integer(val),
190 Err(e) => RespValue::Error(format!("ERR {}", e)),
191 },
192
193 Command::IncrBy { key, delta } => match self.store.atomic_increment(&key, delta) {
194 Ok(val) => RespValue::Integer(val),
195 Err(e) => RespValue::Error(format!("ERR {}", e)),
196 },
197
198 Command::Decr(key) => match self.store.atomic_increment(&key, -1) {
199 Ok(val) => RespValue::Integer(val),
200 Err(e) => RespValue::Error(format!("ERR {}", e)),
201 },
202
203 Command::DecrBy { key, delta } => match self.store.atomic_increment(&key, -delta) {
204 Ok(val) => RespValue::Integer(val),
205 Err(e) => RespValue::Error(format!("ERR {}", e)),
206 },
207
208 Command::Expire { key, seconds } => match self.store.update_ttl(&key, seconds) {
209 Ok(_) => RespValue::Integer(1),
210 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(0),
211 Err(e) => RespValue::Error(format!("ERR {}", e)),
212 },
213
214 Command::PExpire { key, milliseconds } => {
215 match self.store.update_ttl(&key, milliseconds / 1000) {
216 Ok(_) => RespValue::Integer(1),
217 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(0),
218 Err(e) => RespValue::Error(format!("ERR {}", e)),
219 }
220 }
221
222 Command::Ttl(key) => {
223 match self.store.get_ttl(&key) {
224 Ok(Some(ttl)) => RespValue::Integer(ttl as i64),
225 Ok(None) => RespValue::Integer(-1), Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(-2),
227 Err(e) => RespValue::Error(format!("ERR {}", e)),
228 }
229 }
230
231 Command::PTtl(key) => {
232 match self.store.get_ttl(&key) {
233 Ok(Some(ttl)) => RespValue::Integer((ttl * 1000) as i64),
234 Ok(None) => RespValue::Integer(-1), Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(-2),
236 Err(e) => RespValue::Error(format!("ERR {}", e)),
237 }
238 }
239
240 Command::Persist(key) => match self.store.persist(&key) {
241 Ok(_) => RespValue::Integer(1),
242 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(0),
243 Err(e) => RespValue::Error(format!("ERR {}", e)),
244 },
245
246 Command::MGet(keys) => {
247 let values: Vec<RespValue> = keys
248 .into_iter()
249 .map(|key| match self.store.get_bytes(&key) {
250 Ok(value) => RespValue::BulkString(Some(value)),
251 Err(_) => RespValue::BulkString(None),
252 })
253 .collect();
254 RespValue::Array(Some(values))
255 }
256
257 Command::MSet(pairs) => {
258 for (key, value) in pairs {
259 if let Err(e) = self.store.insert_with_timestamp(&key, &value, None) {
261 return RespValue::Error(format!("ERR {}", e));
262 }
263 }
264 RespValue::SimpleString(Bytes::from_static(b"OK"))
265 }
266
267 Command::Ping(msg) => match msg {
268 Some(m) => RespValue::BulkString(Some(m)),
269 None => RespValue::SimpleString(Bytes::from_static(b"PONG")),
270 },
271
272 Command::Echo(msg) => RespValue::BulkString(Some(msg)),
273
274 Command::Config { action, args } => {
275 match action.to_uppercase().as_str() {
276 "GET" => {
277 if args.is_empty() {
279 RespValue::Array(Some(vec![]))
280 } else {
281 let mut results = Vec::new();
283 for arg in args {
284 results.push(RespValue::BulkString(Some(arg)));
285 results.push(RespValue::BulkString(None)); }
287 RespValue::Array(Some(results))
288 }
289 }
290 "SET" => {
291 RespValue::SimpleString(Bytes::from_static(b"OK"))
293 }
294 _ => RespValue::Error(format!("ERR Unknown CONFIG subcommand '{}'", action)),
295 }
296 }
297
298 Command::Command => {
299 let commands = vec![
302 vec![
304 RespValue::BulkString(Some(Bytes::from_static(b"GET"))),
305 RespValue::Integer(2), RespValue::Array(Some(vec![RespValue::BulkString(Some(
307 Bytes::from_static(b"readonly"),
308 ))])),
309 RespValue::Integer(1), RespValue::Integer(1), RespValue::Integer(1), ],
313 vec![
314 RespValue::BulkString(Some(Bytes::from_static(b"SET"))),
315 RespValue::Integer(-3), RespValue::Array(Some(vec![RespValue::BulkString(Some(
317 Bytes::from_static(b"write"),
318 ))])),
319 RespValue::Integer(1),
320 RespValue::Integer(1),
321 RespValue::Integer(1),
322 ],
323 vec![
324 RespValue::BulkString(Some(Bytes::from_static(b"DEL"))),
325 RespValue::Integer(-2), RespValue::Array(Some(vec![RespValue::BulkString(Some(
327 Bytes::from_static(b"write"),
328 ))])),
329 RespValue::Integer(1),
330 RespValue::Integer(-1), RespValue::Integer(1),
332 ],
333 vec![
334 RespValue::BulkString(Some(Bytes::from_static(b"EXISTS"))),
335 RespValue::Integer(-2),
336 RespValue::Array(Some(vec![RespValue::BulkString(Some(
337 Bytes::from_static(b"readonly"),
338 ))])),
339 RespValue::Integer(1),
340 RespValue::Integer(-1),
341 RespValue::Integer(1),
342 ],
343 vec![
345 RespValue::BulkString(Some(Bytes::from_static(b"INCR"))),
346 RespValue::Integer(2),
347 RespValue::Array(Some(vec![RespValue::BulkString(Some(
348 Bytes::from_static(b"write"),
349 ))])),
350 RespValue::Integer(1),
351 RespValue::Integer(1),
352 RespValue::Integer(1),
353 ],
354 vec![
355 RespValue::BulkString(Some(Bytes::from_static(b"DECR"))),
356 RespValue::Integer(2),
357 RespValue::Array(Some(vec![RespValue::BulkString(Some(
358 Bytes::from_static(b"write"),
359 ))])),
360 RespValue::Integer(1),
361 RespValue::Integer(1),
362 RespValue::Integer(1),
363 ],
364 vec![
366 RespValue::BulkString(Some(Bytes::from_static(b"EXPIRE"))),
367 RespValue::Integer(3),
368 RespValue::Array(Some(vec![RespValue::BulkString(Some(
369 Bytes::from_static(b"write"),
370 ))])),
371 RespValue::Integer(1),
372 RespValue::Integer(1),
373 RespValue::Integer(1),
374 ],
375 vec![
376 RespValue::BulkString(Some(Bytes::from_static(b"TTL"))),
377 RespValue::Integer(2),
378 RespValue::Array(Some(vec![RespValue::BulkString(Some(
379 Bytes::from_static(b"readonly"),
380 ))])),
381 RespValue::Integer(1),
382 RespValue::Integer(1),
383 RespValue::Integer(1),
384 ],
385 vec![
387 RespValue::BulkString(Some(Bytes::from_static(b"MGET"))),
388 RespValue::Integer(-2),
389 RespValue::Array(Some(vec![RespValue::BulkString(Some(
390 Bytes::from_static(b"readonly"),
391 ))])),
392 RespValue::Integer(1),
393 RespValue::Integer(-1),
394 RespValue::Integer(1),
395 ],
396 vec![
397 RespValue::BulkString(Some(Bytes::from_static(b"MSET"))),
398 RespValue::Integer(-3),
399 RespValue::Array(Some(vec![RespValue::BulkString(Some(
400 Bytes::from_static(b"write"),
401 ))])),
402 RespValue::Integer(1),
403 RespValue::Integer(-1),
404 RespValue::Integer(2), ],
406 vec![
408 RespValue::BulkString(Some(Bytes::from_static(b"PING"))),
409 RespValue::Integer(-1),
410 RespValue::Array(Some(vec![RespValue::BulkString(Some(
411 Bytes::from_static(b"fast"),
412 ))])),
413 RespValue::Integer(0),
414 RespValue::Integer(0),
415 RespValue::Integer(0),
416 ],
417 vec![
419 RespValue::BulkString(Some(Bytes::from_static(b"JSONPATCH"))),
420 RespValue::Integer(3),
421 RespValue::Array(Some(vec![RespValue::BulkString(Some(
422 Bytes::from_static(b"write"),
423 ))])),
424 RespValue::Integer(1),
425 RespValue::Integer(1),
426 RespValue::Integer(1),
427 ],
428 vec![
429 RespValue::BulkString(Some(Bytes::from_static(b"CAS"))),
430 RespValue::Integer(4),
431 RespValue::Array(Some(vec![RespValue::BulkString(Some(
432 Bytes::from_static(b"write"),
433 ))])),
434 RespValue::Integer(1),
435 RespValue::Integer(1),
436 RespValue::Integer(1),
437 ],
438 ];
439
440 RespValue::Array(Some(
441 commands
442 .into_iter()
443 .map(|cmd| RespValue::Array(Some(cmd)))
444 .collect(),
445 ))
446 }
447
448 Command::Quit => RespValue::SimpleString(Bytes::from_static(b"OK")),
449
450 Command::FlushDb => {
451 RespValue::Error("ERR FLUSHDB requires server restart. For persistent mode, also delete data files.".to_string())
456 }
457
458 Command::Keys(pattern) => {
459 let prefix = extract_prefix(&pattern);
461
462 let (start_key, end_key) = if prefix.is_empty() {
464 (vec![], vec![0xFF; 255])
466 } else if pattern == prefix {
467 return match self.store.get_bytes(prefix.as_bytes()) {
469 Ok(_) => {
470 let keys =
471 vec![RespValue::BulkString(Some(Bytes::from(prefix.to_string())))];
472 RespValue::Array(Some(keys))
473 }
474 Err(_) => RespValue::Array(Some(vec![])),
475 };
476 } else {
477 let mut end = prefix.as_bytes().to_vec();
479 end.push(b'~'); (prefix.as_bytes().to_vec(), end)
481 };
482
483 match self.store.range_query(&start_key, &end_key, 100000) {
485 Ok(pairs) => {
486 let keys: Vec<RespValue> = pairs
487 .into_iter()
488 .filter(|(key, _)| match_pattern(key, &pattern))
489 .map(|(key, _)| RespValue::BulkString(Some(Bytes::from(key))))
490 .collect();
491 RespValue::Array(Some(keys))
492 }
493 Err(e) => RespValue::Error(format!("ERR {}", e)),
494 }
495 }
496
497 Command::Scan {
498 cursor,
499 count,
500 pattern,
501 } => {
502 let start_key = if cursor.is_empty() || cursor == b"0" {
504 vec![]
505 } else {
506 cursor.clone()
507 };
508
509 let (scan_start, scan_end) = if let Some(ref pat) = pattern {
511 let prefix = extract_prefix(pat);
512 if !prefix.is_empty() && pat.starts_with(prefix) && pat.contains('*') {
513 let mut end = prefix.as_bytes().to_vec();
515 end.push(b'~');
516
517 let actual_start = if start_key.len() > prefix.len()
519 && start_key.starts_with(prefix.as_bytes())
520 {
521 start_key
522 } else if start_key.is_empty() {
523 prefix.as_bytes().to_vec()
524 } else {
525 start_key
526 };
527
528 (actual_start, end)
529 } else {
530 (start_key, vec![0xFF; 255])
531 }
532 } else {
533 (start_key, vec![0xFF; 255])
534 };
535
536 let fetch_count = if pattern.is_some() { count * 2 } else { count };
538 match self
539 .store
540 .range_query(&scan_start, &scan_end, fetch_count + 1)
541 {
542 Ok(pairs) => {
543 let mut keys = Vec::new();
544 let mut next_cursor = None;
545
546 for (key, _) in pairs.into_iter() {
547 if keys.len() >= count {
549 next_cursor = Some(key.clone());
550 break;
551 }
552
553 if let Some(ref pat) = pattern {
555 if !match_pattern(&key, pat) {
556 continue;
557 }
558 }
559
560 keys.push(RespValue::BulkString(Some(Bytes::from(key.clone()))));
561 }
562
563 let cursor_str = if let Some(next) = next_cursor {
565 Bytes::from(next)
566 } else {
567 Bytes::from_static(b"0") };
569
570 RespValue::Array(Some(vec![
571 RespValue::BulkString(Some(cursor_str)),
572 RespValue::Array(Some(keys)),
573 ]))
574 }
575 Err(e) => RespValue::Error(format!("ERR {}", e)),
576 }
577 }
578
579 Command::Info(section) => {
580 use std::sync::atomic::Ordering;
581
582 let uptime = self.start_time.elapsed().as_secs();
584 let commands = self.commands_processed.load(Ordering::Relaxed);
585 let stats = self.store.stats();
586
587 let format_bytes = |bytes: usize| -> String {
589 if bytes < 1024 {
590 format!("{}B", bytes)
591 } else if bytes < 1024 * 1024 {
592 format!("{:.1}K", bytes as f64 / 1024.0)
593 } else if bytes < 1024 * 1024 * 1024 {
594 format!("{:.1}M", bytes as f64 / (1024.0 * 1024.0))
595 } else {
596 format!("{:.1}G", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
597 }
598 };
599
600 let mut info = String::new();
601
602 if section.is_none()
604 || section
605 .as_ref()
606 .map(|s| s.eq_ignore_ascii_case("server"))
607 .unwrap_or(false)
608 {
609 info.push_str(&format!(
610 "# Server\r\n\
611 redis_version:feox-{}\r\n\
612 redis_mode:standalone\r\n\
613 process_id:{}\r\n\
614 tcp_port:6379\r\n\
615 uptime_in_seconds:{}\r\n",
616 env!("CARGO_PKG_VERSION"),
617 std::process::id(),
618 uptime
619 ));
620 }
621
622 if section.is_none()
624 || section
625 .as_ref()
626 .map(|s| s.eq_ignore_ascii_case("memory"))
627 .unwrap_or(false)
628 {
629 info.push_str(&format!(
630 "# Memory\r\n\
631 used_memory:{}\r\n\
632 used_memory_human:{}\r\n\
633 used_memory_cache:{}\r\n\
634 used_memory_cache_human:{}\r\n",
635 stats.memory_usage,
636 format_bytes(stats.memory_usage),
637 stats.cache_memory,
638 format_bytes(stats.cache_memory)
639 ));
640 }
641
642 if section.is_none()
644 || section
645 .as_ref()
646 .map(|s| s.eq_ignore_ascii_case("stats"))
647 .unwrap_or(false)
648 {
649 info.push_str(&format!(
650 "# Stats\r\n\
651 total_connections_received:0\r\n\
652 total_commands_processed:{}\r\n\
653 instantaneous_ops_per_sec:0\r\n\
654 total_net_input_bytes:0\r\n\
655 total_net_output_bytes:0\r\n\
656 total_operations:{}\r\n\
657 total_gets:{}\r\n\
658 total_inserts:{}\r\n\
659 keyspace_hits:{}\r\n\
660 keyspace_misses:{}\r\n\
661 cache_hit_rate:{:.2}\r\n",
662 commands,
663 stats.total_operations,
664 stats.total_gets,
665 stats.total_inserts,
666 stats.cache_hits,
667 stats.cache_misses,
668 stats.cache_hit_rate * 100.0
669 ));
670 }
671
672 if section.is_none()
674 || section
675 .as_ref()
676 .map(|s| s.eq_ignore_ascii_case("keyspace"))
677 .unwrap_or(false)
678 {
679 info.push_str(&format!(
680 "# Keyspace\r\n\
681 db0:keys={},expires=0,avg_ttl=0\r\n",
682 stats.record_count
683 ));
684 }
685
686 RespValue::BulkString(Some(Bytes::from(info)))
687 }
688
689 Command::JsonPatch { key, patch } => {
690 match self.store.json_patch(&key, &patch) {
692 Ok(_) => RespValue::SimpleString(Bytes::from_static(b"OK")),
693 Err(e) => RespValue::Error(format!("ERR {}", e)),
694 }
695 }
696
697 Command::Cas {
698 key,
699 expected,
700 new_value,
701 } => {
702 match self.store.compare_and_swap(&key, &expected, &new_value) {
704 Ok(swapped) => RespValue::Integer(if swapped { 1 } else { 0 }),
705 Err(e) => RespValue::Error(format!("ERR {}", e)),
706 }
707 }
708
709 Command::LPush { key, values } => match self.list_ops.lpush(&key, values) {
710 Ok(count) => RespValue::Integer(count),
711 Err(e) => RespValue::Error(format!("ERR {}", e)),
712 },
713
714 Command::RPush { key, values } => match self.list_ops.rpush(&key, values) {
715 Ok(count) => RespValue::Integer(count),
716 Err(e) => RespValue::Error(format!("ERR {}", e)),
717 },
718
719 Command::LPop { key, count } => match self.list_ops.lpop(&key, count) {
720 Ok(values) => {
721 if values.is_empty() {
722 RespValue::BulkString(None)
723 } else if values.len() == 1 {
724 RespValue::BulkString(Some(values.into_iter().next().unwrap()))
725 } else {
726 RespValue::Array(Some(
727 values
728 .into_iter()
729 .map(|v| RespValue::BulkString(Some(v)))
730 .collect(),
731 ))
732 }
733 }
734 Err(e) => RespValue::Error(format!("ERR {}", e)),
735 },
736
737 Command::RPop { key, count } => match self.list_ops.rpop(&key, count) {
738 Ok(values) => {
739 if values.is_empty() {
740 RespValue::BulkString(None)
741 } else if values.len() == 1 {
742 RespValue::BulkString(Some(values.into_iter().next().unwrap()))
743 } else {
744 RespValue::Array(Some(
745 values
746 .into_iter()
747 .map(|v| RespValue::BulkString(Some(v)))
748 .collect(),
749 ))
750 }
751 }
752 Err(e) => RespValue::Error(format!("ERR {}", e)),
753 },
754
755 Command::LLen(key) => match self.list_ops.llen(&key) {
756 Ok(count) => RespValue::Integer(count),
757 Err(e) => RespValue::Error(format!("ERR {}", e)),
758 },
759
760 Command::LRange { key, start, stop } => match self.list_ops.lrange(&key, start, stop) {
761 Ok(values) => RespValue::Array(Some(
762 values
763 .into_iter()
764 .map(|v| RespValue::BulkString(Some(v)))
765 .collect(),
766 )),
767 Err(e) => RespValue::Error(format!("ERR {}", e)),
768 },
769
770 Command::LIndex { key, index } => match self.list_ops.lindex(&key, index) {
771 Ok(Some(value)) => RespValue::BulkString(Some(value)),
772 Ok(None) => RespValue::BulkString(None),
773 Err(e) => RespValue::Error(format!("ERR {}", e)),
774 },
775
776 Command::Auth(_) => {
777 if self.config.requirepass.is_none() {
780 RespValue::Error("-ERR Client sent AUTH, but no password is set".to_string())
781 } else {
782 RespValue::Error("-ERR AUTH failed".to_string())
784 }
785 }
786
787 Command::Client {
788 ref subcommand,
789 ref args,
790 } => self
791 .client_ops
792 .execute(subcommand, args, self.connection_id),
793
794 Command::Subscribe(_)
796 | Command::Unsubscribe(_)
797 | Command::PSubscribe(_)
798 | Command::PUnsubscribe(_)
799 | Command::Publish { .. }
800 | Command::PubSub { .. } => RespValue::Error(
801 "-ERR Pub/Sub commands should be handled in connection layer".to_string(),
802 ),
803
804 Command::Multi
806 | Command::Exec
807 | Command::Discard
808 | Command::Watch(_)
809 | Command::Unwatch => RespValue::Error(
810 "-ERR Transaction commands should be handled in connection layer".to_string(),
811 ),
812 }
813 }
814}