1use super::client::ClientOperations;
2use super::command_table::{CommandDef, COMMAND_TABLE};
3use super::hash::HashOperations;
4use super::list::ListOperations;
5use super::Command;
6use crate::client_registry::ClientRegistry;
7use crate::config::Config;
8use crate::protocol::resp::RespValue;
9use crate::watch_registry::WatchRegistry;
10use bytes::Bytes;
11use feoxdb::FeoxStore;
12use std::sync::Arc;
13
14fn match_pattern(key: &[u8], pattern: &str) -> bool {
16 let key_str = String::from_utf8_lossy(key);
17 glob_match(pattern, &key_str)
18}
19
20fn glob_match(pattern: &str, text: &str) -> bool {
22 let mut p_idx = 0;
23 let mut t_idx = 0;
24 let mut star_idx = None;
25 let mut star_match = None;
26
27 let pattern_bytes = pattern.as_bytes();
28 let text_bytes = text.as_bytes();
29
30 while t_idx < text_bytes.len() {
31 if p_idx < pattern_bytes.len() {
32 match pattern_bytes[p_idx] {
33 b'*' => {
34 star_idx = Some(p_idx);
35 star_match = Some(t_idx);
36 p_idx += 1;
37 }
38 b'?' => {
39 p_idx += 1;
40 t_idx += 1;
41 }
42 _ => {
43 if pattern_bytes[p_idx] == text_bytes[t_idx] {
44 p_idx += 1;
45 t_idx += 1;
46 } else if let Some(star) = star_idx {
47 p_idx = star + 1;
48 star_match = Some(star_match.unwrap() + 1);
49 t_idx = star_match.unwrap();
50 } else {
51 return false;
52 }
53 }
54 }
55 } else if let Some(star) = star_idx {
56 p_idx = star + 1;
57 star_match = Some(star_match.unwrap() + 1);
58 t_idx = star_match.unwrap();
59 } else {
60 return false;
61 }
62 }
63
64 while p_idx < pattern_bytes.len() && pattern_bytes[p_idx] == b'*' {
66 p_idx += 1;
67 }
68
69 p_idx == pattern_bytes.len()
70}
71
72fn extract_prefix(pattern: &str) -> &str {
74 for (i, ch) in pattern.char_indices() {
75 if ch == '*' || ch == '?' || ch == '[' {
76 return &pattern[..i];
77 }
78 }
79 pattern
80}
81
82#[derive(Clone)]
86pub struct CommandExecutor {
87 store: Arc<FeoxStore>,
88 list_ops: ListOperations,
89 hash_ops: HashOperations,
90 client_ops: ClientOperations,
91 config: Config, start_time: std::time::Instant,
93 commands_processed: Arc<std::sync::atomic::AtomicU64>,
94 connection_id: Option<usize>,
95}
96
97impl CommandExecutor {
98 pub fn new(store: Arc<FeoxStore>, config: &Config) -> Self {
100 let list_ops = ListOperations::new(Arc::clone(&store));
101 let hash_ops = HashOperations::new(Arc::clone(&store));
102 Self {
103 store,
104 list_ops,
105 hash_ops,
106 client_ops: ClientOperations::new(),
107 config: config.clone(),
108 start_time: std::time::Instant::now(),
109 commands_processed: Arc::new(std::sync::atomic::AtomicU64::new(0)),
110 connection_id: None,
111 }
112 }
113
114 pub fn with_client_info(mut self, registry: Arc<ClientRegistry>, connection_id: usize) -> Self {
116 self.client_ops = ClientOperations::with_registry(registry);
117 self.connection_id = Some(connection_id);
118 self
119 }
120
121 pub fn check_auth(&self, password: &str) -> bool {
123 self.config.check_password(password)
124 }
125
126 #[inline(always)]
128 pub fn fast_set(&self, key: &[u8], value: &[u8]) -> Result<(), feoxdb::FeoxError> {
129 self.store.insert_with_timestamp(key, value, None)?;
130 WatchRegistry::notify_key_modified(key);
131 Ok(())
132 }
133
134 #[inline(always)]
136 pub fn fast_set_bytes(&self, key: &[u8], value: bytes::Bytes) -> Result<(), feoxdb::FeoxError> {
137 self.store.insert_bytes_with_timestamp(key, value, None)?;
138 WatchRegistry::notify_key_modified(key);
139 Ok(())
140 }
141
142 #[inline(always)]
144 pub fn fast_get(&self, key: &[u8]) -> Result<bytes::Bytes, feoxdb::FeoxError> {
145 self.store.get_bytes(key)
146 }
147
148 #[inline]
150 pub fn execute(&self, cmd: Command) -> RespValue {
151 use std::sync::atomic::Ordering;
152
153 self.commands_processed.fetch_add(1, Ordering::Relaxed);
155
156 match cmd {
157 Command::Get(key) => match self.store.get_bytes(&key) {
158 Ok(value) => RespValue::BulkString(Some(value)),
159 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::BulkString(None),
160 Err(e) => RespValue::Error(format!("ERR {}", e)),
161 },
162
163 Command::Set { key, value, ex, px } => {
164 let result = if let Some(seconds) = ex {
165 self.store
166 .insert_bytes_with_ttl_and_timestamp(&key, value, seconds, None)
167 } else if let Some(millis) = px {
168 self.store
169 .insert_bytes_with_ttl_and_timestamp(&key, value, millis / 1000, None)
170 } else {
171 self.store.insert_bytes_with_timestamp(&key, value, None)
172 };
173
174 match result {
175 Ok(_) => {
176 WatchRegistry::notify_key_modified(&key);
177 RespValue::SimpleString(Bytes::from_static(b"OK"))
178 }
179 Err(e) => RespValue::Error(format!("ERR {}", e)),
180 }
181 }
182
183 Command::Del(keys) => {
184 let mut count = 0i64;
185 for key in &keys {
186 if self.store.delete(key).is_ok() {
187 WatchRegistry::notify_key_modified(key);
188 count += 1;
189 }
190 }
191 RespValue::Integer(count)
192 }
193
194 Command::Exists(keys) => {
195 let count = keys
196 .iter()
197 .filter(|key| self.store.contains_key(key))
198 .count() as i64;
199 RespValue::Integer(count)
200 }
201
202 Command::Incr(key) => self.string_incr(&key, 1),
203
204 Command::IncrBy { key, delta } => self.string_incr(&key, delta),
205
206 Command::Decr(key) => self.string_incr(&key, -1),
207
208 Command::DecrBy { key, delta } => self.string_incr(&key, -delta),
209
210 Command::Expire { key, seconds } => match self.store.update_ttl(&key, seconds) {
211 Ok(_) => RespValue::Integer(1),
212 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(0),
213 Err(e) => RespValue::Error(format!("ERR {}", e)),
214 },
215
216 Command::PExpire { key, milliseconds } => {
217 match self.store.update_ttl(&key, milliseconds / 1000) {
218 Ok(_) => RespValue::Integer(1),
219 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(0),
220 Err(e) => RespValue::Error(format!("ERR {}", e)),
221 }
222 }
223
224 Command::Ttl(key) => {
225 match self.store.get_ttl(&key) {
226 Ok(Some(ttl)) => RespValue::Integer(ttl as i64),
227 Ok(None) => RespValue::Integer(-1), Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(-2),
229 Err(e) => RespValue::Error(format!("ERR {}", e)),
230 }
231 }
232
233 Command::PTtl(key) => {
234 match self.store.get_ttl(&key) {
235 Ok(Some(ttl)) => RespValue::Integer((ttl * 1000) as i64),
236 Ok(None) => RespValue::Integer(-1), Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(-2),
238 Err(e) => RespValue::Error(format!("ERR {}", e)),
239 }
240 }
241
242 Command::Persist(key) => match self.store.persist(&key) {
243 Ok(_) => RespValue::Integer(1),
244 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(0),
245 Err(e) => RespValue::Error(format!("ERR {}", e)),
246 },
247
248 Command::MGet(keys) => {
249 let values: Vec<RespValue> = keys
250 .into_iter()
251 .map(|key| match self.store.get_bytes(&key) {
252 Ok(value) => RespValue::BulkString(Some(value)),
253 Err(_) => RespValue::BulkString(None),
254 })
255 .collect();
256 RespValue::Array(Some(values))
257 }
258
259 Command::MSet(pairs) => {
260 for (key, value) in &pairs {
261 if let Err(e) = self.store.insert_with_timestamp(key, value, None) {
263 return RespValue::Error(format!("ERR {}", e));
264 }
265 WatchRegistry::notify_key_modified(key);
266 }
267 RespValue::SimpleString(Bytes::from_static(b"OK"))
268 }
269
270 Command::Ping(msg) => match msg {
271 Some(m) => RespValue::BulkString(Some(m)),
272 None => RespValue::SimpleString(Bytes::from_static(b"PONG")),
273 },
274
275 Command::Echo(msg) => RespValue::BulkString(Some(msg)),
276
277 Command::Config { action, args } => {
278 match action.to_uppercase().as_str() {
279 "GET" => {
280 if args.is_empty() {
282 RespValue::Array(Some(vec![]))
283 } else {
284 let mut results = Vec::new();
285 for arg in args {
286 let key = String::from_utf8_lossy(&arg).to_lowercase();
287 let value = match key.as_str() {
288 "maxmemory" => "0",
289 "maxclients" => "10000",
290 "timeout" => "0",
291 "tcp-keepalive" => "300",
292 "databases" => "16",
293 _ => "0",
294 };
295 results.push(RespValue::BulkString(Some(arg)));
296 results.push(RespValue::BulkString(Some(Bytes::from(value))));
297 }
298 RespValue::Array(Some(results))
299 }
300 }
301 "SET" => {
302 RespValue::SimpleString(Bytes::from_static(b"OK"))
304 }
305 _ => RespValue::Error(format!("ERR Unknown CONFIG subcommand '{}'", action)),
306 }
307 }
308
309 Command::Command { subcommand, args } => {
310 Self::handle_command_subcommand(subcommand, args)
311 }
312
313 Command::Quit => RespValue::SimpleString(Bytes::from_static(b"OK")),
314
315 Command::FlushDb => {
316 RespValue::Error("ERR FLUSHDB requires server restart. For persistent mode, also delete data files.".to_string())
321 }
322
323 Command::Keys(pattern) => {
324 let prefix = extract_prefix(&pattern);
326
327 let (start_key, end_key) = if prefix.is_empty() {
329 (vec![], vec![0xFF; 255])
331 } else if pattern == prefix {
332 return match self.store.get_bytes(prefix.as_bytes()) {
334 Ok(_) => {
335 let keys =
336 vec![RespValue::BulkString(Some(Bytes::from(prefix.to_string())))];
337 RespValue::Array(Some(keys))
338 }
339 Err(_) => RespValue::Array(Some(vec![])),
340 };
341 } else {
342 let mut end = prefix.as_bytes().to_vec();
344 end.push(b'~'); (prefix.as_bytes().to_vec(), end)
346 };
347
348 match self.store.range_query(&start_key, &end_key, 100000) {
350 Ok(pairs) => {
351 let keys: Vec<RespValue> = pairs
352 .into_iter()
353 .filter(|(key, _)| match_pattern(key, &pattern))
354 .map(|(key, _)| RespValue::BulkString(Some(Bytes::from(key))))
355 .collect();
356 RespValue::Array(Some(keys))
357 }
358 Err(e) => RespValue::Error(format!("ERR {}", e)),
359 }
360 }
361
362 Command::Scan {
363 cursor,
364 count,
365 pattern,
366 } => {
367 let start_key = if cursor.is_empty() || cursor == b"0" {
369 vec![]
370 } else {
371 cursor.clone()
372 };
373
374 let (scan_start, scan_end) = if let Some(ref pat) = pattern {
376 let prefix = extract_prefix(pat);
377 if !prefix.is_empty() && pat.starts_with(prefix) && pat.contains('*') {
378 let mut end = prefix.as_bytes().to_vec();
380 end.push(b'~');
381
382 let actual_start = if start_key.len() > prefix.len()
384 && start_key.starts_with(prefix.as_bytes())
385 {
386 start_key
387 } else if start_key.is_empty() {
388 prefix.as_bytes().to_vec()
389 } else {
390 start_key
391 };
392
393 (actual_start, end)
394 } else {
395 (start_key, vec![0xFF; 255])
396 }
397 } else {
398 (start_key, vec![0xFF; 255])
399 };
400
401 let fetch_count = if pattern.is_some() { count * 2 } else { count };
403 match self
404 .store
405 .range_query(&scan_start, &scan_end, fetch_count + 1)
406 {
407 Ok(pairs) => {
408 let mut keys = Vec::new();
409 let mut next_cursor = None;
410
411 for (key, _) in pairs.into_iter() {
412 if keys.len() >= count {
414 next_cursor = Some(key.clone());
415 break;
416 }
417
418 if let Some(ref pat) = pattern {
420 if !match_pattern(&key, pat) {
421 continue;
422 }
423 }
424
425 keys.push(RespValue::BulkString(Some(Bytes::from(key.clone()))));
426 }
427
428 let cursor_str = if let Some(next) = next_cursor {
430 Bytes::from(next)
431 } else {
432 Bytes::from_static(b"0") };
434
435 RespValue::Array(Some(vec![
436 RespValue::BulkString(Some(cursor_str)),
437 RespValue::Array(Some(keys)),
438 ]))
439 }
440 Err(e) => RespValue::Error(format!("ERR {}", e)),
441 }
442 }
443
444 Command::Info(section) => {
445 use std::sync::atomic::Ordering;
446
447 let uptime = self.start_time.elapsed().as_secs();
449 let commands = self.commands_processed.load(Ordering::Relaxed);
450 let stats = self.store.stats();
451
452 let format_bytes = |bytes: usize| -> String {
454 if bytes < 1024 {
455 format!("{}B", bytes)
456 } else if bytes < 1024 * 1024 {
457 format!("{:.1}K", bytes as f64 / 1024.0)
458 } else if bytes < 1024 * 1024 * 1024 {
459 format!("{:.1}M", bytes as f64 / (1024.0 * 1024.0))
460 } else {
461 format!("{:.1}G", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
462 }
463 };
464
465 let mut info = String::new();
466
467 if section.is_none()
469 || section
470 .as_ref()
471 .map(|s| s.eq_ignore_ascii_case("server"))
472 .unwrap_or(false)
473 {
474 info.push_str(&format!(
475 "# Server\r\n\
476 redis_version:feox-{}\r\n\
477 redis_mode:standalone\r\n\
478 arch_bits:{}\r\n\
479 process_id:{}\r\n\
480 tcp_port:6379\r\n\
481 uptime_in_seconds:{}\r\n",
482 env!("CARGO_PKG_VERSION"),
483 std::mem::size_of::<usize>() * 8,
484 std::process::id(),
485 uptime
486 ));
487 }
488
489 if section.is_none()
491 || section
492 .as_ref()
493 .map(|s| s.eq_ignore_ascii_case("memory"))
494 .unwrap_or(false)
495 {
496 info.push_str(&format!(
497 "# Memory\r\n\
498 used_memory:{}\r\n\
499 used_memory_human:{}\r\n\
500 used_memory_cache:{}\r\n\
501 used_memory_cache_human:{}\r\n",
502 stats.memory_usage,
503 format_bytes(stats.memory_usage),
504 stats.cache_memory,
505 format_bytes(stats.cache_memory)
506 ));
507 }
508
509 if section.is_none()
511 || section
512 .as_ref()
513 .map(|s| s.eq_ignore_ascii_case("stats"))
514 .unwrap_or(false)
515 {
516 info.push_str(&format!(
517 "# Stats\r\n\
518 total_connections_received:0\r\n\
519 total_commands_processed:{}\r\n\
520 instantaneous_ops_per_sec:0\r\n\
521 total_net_input_bytes:0\r\n\
522 total_net_output_bytes:0\r\n\
523 total_operations:{}\r\n\
524 total_gets:{}\r\n\
525 total_inserts:{}\r\n\
526 keyspace_hits:{}\r\n\
527 keyspace_misses:{}\r\n\
528 cache_hit_rate:{:.2}\r\n",
529 commands,
530 stats.total_operations,
531 stats.total_gets,
532 stats.total_inserts,
533 stats.cache_hits,
534 stats.cache_misses,
535 stats.cache_hit_rate * 100.0
536 ));
537 }
538
539 if section.is_none()
541 || section
542 .as_ref()
543 .map(|s| s.eq_ignore_ascii_case("keyspace"))
544 .unwrap_or(false)
545 {
546 info.push_str(&format!(
547 "# Keyspace\r\n\
548 db0:keys={},expires=0,avg_ttl=0\r\n",
549 stats.record_count
550 ));
551 }
552
553 RespValue::BulkString(Some(Bytes::from(info)))
554 }
555
556 Command::JsonPatch { key, patch } => {
557 match self.store.json_patch(&key, &patch) {
559 Ok(_) => RespValue::SimpleString(Bytes::from_static(b"OK")),
560 Err(e) => RespValue::Error(format!("ERR {}", e)),
561 }
562 }
563
564 Command::Cas {
565 key,
566 expected,
567 new_value,
568 } => {
569 match self.store.compare_and_swap(&key, &expected, &new_value) {
571 Ok(swapped) => RespValue::Integer(if swapped { 1 } else { 0 }),
572 Err(e) => RespValue::Error(format!("ERR {}", e)),
573 }
574 }
575
576 Command::LPush { key, values } => match self.list_ops.lpush(&key, values) {
577 Ok(count) => {
578 WatchRegistry::notify_key_modified(&key);
579 RespValue::Integer(count)
580 }
581 Err(e) => RespValue::Error(format!("ERR {}", e)),
582 },
583
584 Command::RPush { key, values } => match self.list_ops.rpush(&key, values) {
585 Ok(count) => {
586 WatchRegistry::notify_key_modified(&key);
587 RespValue::Integer(count)
588 }
589 Err(e) => RespValue::Error(format!("ERR {}", e)),
590 },
591
592 Command::LPop { key, count } => match self.list_ops.lpop(&key, count) {
593 Ok(values) => {
594 if values.is_empty() {
595 RespValue::BulkString(None)
596 } else {
597 WatchRegistry::notify_key_modified(&key);
598 if values.len() == 1 {
599 RespValue::BulkString(Some(values.into_iter().next().unwrap()))
600 } else {
601 RespValue::Array(Some(
602 values
603 .into_iter()
604 .map(|v| RespValue::BulkString(Some(v)))
605 .collect(),
606 ))
607 }
608 }
609 }
610 Err(e) => RespValue::Error(format!("ERR {}", e)),
611 },
612
613 Command::RPop { key, count } => match self.list_ops.rpop(&key, count) {
614 Ok(values) => {
615 if values.is_empty() {
616 RespValue::BulkString(None)
617 } else {
618 WatchRegistry::notify_key_modified(&key);
619 if values.len() == 1 {
620 RespValue::BulkString(Some(values.into_iter().next().unwrap()))
621 } else {
622 RespValue::Array(Some(
623 values
624 .into_iter()
625 .map(|v| RespValue::BulkString(Some(v)))
626 .collect(),
627 ))
628 }
629 }
630 }
631 Err(e) => RespValue::Error(format!("ERR {}", e)),
632 },
633
634 Command::LLen(key) => match self.list_ops.llen(&key) {
635 Ok(count) => RespValue::Integer(count),
636 Err(e) => RespValue::Error(format!("ERR {}", e)),
637 },
638
639 Command::LRange { key, start, stop } => match self.list_ops.lrange(&key, start, stop) {
640 Ok(values) => RespValue::Array(Some(
641 values
642 .into_iter()
643 .map(|v| RespValue::BulkString(Some(v)))
644 .collect(),
645 )),
646 Err(e) => RespValue::Error(format!("ERR {}", e)),
647 },
648
649 Command::LIndex { key, index } => match self.list_ops.lindex(&key, index) {
650 Ok(Some(value)) => RespValue::BulkString(Some(value)),
651 Ok(None) => RespValue::BulkString(None),
652 Err(e) => RespValue::Error(format!("ERR {}", e)),
653 },
654
655 Command::HSet { key, fields } => {
656 let field_refs = fields.iter().map(|(f, v)| (f.as_slice(), v.clone()));
657 match self.hash_ops.hset(&key, field_refs) {
658 Ok(count) => {
659 WatchRegistry::notify_key_modified(&key);
660 RespValue::Integer(count)
661 }
662 Err(e) => RespValue::Error(format!("ERR {}", e)),
663 }
664 }
665
666 Command::HGet { key, field } => match self.hash_ops.hget(&key, &field) {
667 Ok(Some(value)) => RespValue::BulkString(Some(value)),
668 Ok(None) => RespValue::BulkString(None),
669 Err(e) => RespValue::Error(format!("ERR {}", e)),
670 },
671
672 Command::HMGet { key, fields } => match self.hash_ops.hmget(&key, fields) {
673 Ok(values) => RespValue::Array(Some(
674 values
675 .into_iter()
676 .map(|v| match v {
677 Some(val) => RespValue::BulkString(Some(val)),
678 None => RespValue::BulkString(None),
679 })
680 .collect(),
681 )),
682 Err(e) => RespValue::Error(format!("ERR {}", e)),
683 },
684
685 Command::HDel { key, fields } => match self.hash_ops.hdel(&key, fields) {
686 Ok(count) => {
687 if count > 0 {
688 WatchRegistry::notify_key_modified(&key);
689 }
690 RespValue::Integer(count)
691 }
692 Err(e) => RespValue::Error(format!("ERR {}", e)),
693 },
694
695 Command::HExists { key, field } => match self.hash_ops.hexists(&key, &field) {
696 Ok(exists) => RespValue::Integer(if exists { 1 } else { 0 }),
697 Err(e) => RespValue::Error(format!("ERR {}", e)),
698 },
699
700 Command::HGetAll(key) => match self.hash_ops.hgetall(&key) {
701 Ok(pairs) => {
702 let mut result = Vec::new();
703 for (field, value) in pairs {
704 result.push(RespValue::BulkString(Some(Bytes::from(field))));
705 result.push(RespValue::BulkString(Some(value)));
706 }
707 RespValue::Array(Some(result))
708 }
709 Err(e) => RespValue::Error(format!("ERR {}", e)),
710 },
711
712 Command::HLen(key) => match self.hash_ops.hlen(&key) {
713 Ok(count) => RespValue::Integer(count),
714 Err(e) => RespValue::Error(format!("ERR {}", e)),
715 },
716
717 Command::HKeys(key) => match self.hash_ops.hkeys(&key) {
718 Ok(keys) => RespValue::Array(Some(
719 keys.into_iter()
720 .map(|k| RespValue::BulkString(Some(Bytes::from(k))))
721 .collect(),
722 )),
723 Err(e) => RespValue::Error(format!("ERR {}", e)),
724 },
725
726 Command::HVals(key) => match self.hash_ops.hvals(&key) {
727 Ok(vals) => RespValue::Array(Some(
728 vals.into_iter()
729 .map(|v| RespValue::BulkString(Some(v)))
730 .collect(),
731 )),
732 Err(e) => RespValue::Error(format!("ERR {}", e)),
733 },
734
735 Command::HIncrBy { key, field, delta } => {
736 match self.hash_ops.hincrby(&key, &field, delta) {
737 Ok(new_value) => {
738 WatchRegistry::notify_key_modified(&key);
739 RespValue::Integer(new_value)
740 }
741 Err(e) => RespValue::Error(format!("ERR {}", e)),
742 }
743 }
744
745 Command::Auth(_) => {
746 if self.config.requirepass.is_none() {
749 RespValue::Error("-ERR Client sent AUTH, but no password is set".to_string())
750 } else {
751 RespValue::Error("-ERR AUTH failed".to_string())
753 }
754 }
755
756 Command::Client {
757 ref subcommand,
758 ref args,
759 } => self
760 .client_ops
761 .execute(subcommand, args, self.connection_id),
762
763 Command::Subscribe(_)
765 | Command::Unsubscribe(_)
766 | Command::PSubscribe(_)
767 | Command::PUnsubscribe(_)
768 | Command::Publish { .. }
769 | Command::PubSub { .. } => RespValue::Error(
770 "-ERR Pub/Sub commands should be handled in connection layer".to_string(),
771 ),
772
773 Command::Multi
775 | Command::Exec
776 | Command::Discard
777 | Command::Watch(_)
778 | Command::Unwatch => RespValue::Error(
779 "-ERR Transaction commands should be handled in connection layer".to_string(),
780 ),
781 }
782 }
783
784 fn string_incr(&self, key: &[u8], delta: i64) -> RespValue {
785 if let Ok(val) = self.store.atomic_increment(key, delta) {
787 WatchRegistry::notify_key_modified(key);
788 return RespValue::Integer(val);
789 }
790
791 let current: i64 = match self.store.get_bytes(key) {
793 Ok(bytes) => {
794 let s = String::from_utf8_lossy(&bytes);
795 match s.trim().parse::<i64>() {
796 Ok(v) => v,
797 Err(_) => {
798 return RespValue::Error(
799 "ERR value is not an integer or out of range".to_string(),
800 )
801 }
802 }
803 }
804 Err(feoxdb::FeoxError::KeyNotFound) => 0,
805 Err(e) => return RespValue::Error(format!("ERR {}", e)),
806 };
807
808 let new_val = current + delta;
809 match self
810 .store
811 .insert_bytes_with_timestamp(key, Bytes::from(new_val.to_string()), None)
812 {
813 Ok(_) => {
814 WatchRegistry::notify_key_modified(key);
815 RespValue::Integer(new_val)
816 }
817 Err(e) => RespValue::Error(format!("ERR {}", e)),
818 }
819 }
820
821 fn handle_command_subcommand(subcommand: Option<String>, args: Vec<Vec<u8>>) -> RespValue {
822 match subcommand.as_deref() {
823 None => {
824 let commands: Vec<RespValue> = COMMAND_TABLE
825 .iter()
826 .map(|cmd| RespValue::Array(Some(cmd.to_resp_array())))
827 .collect();
828 RespValue::Array(Some(commands))
829 }
830 Some(subcmd) => match subcmd.to_uppercase().as_str() {
831 "COUNT" => {
832 let count = COMMAND_TABLE.len() as i64;
833 RespValue::Integer(count)
834 }
835 "INFO" => {
836 if args.is_empty() {
837 let commands: Vec<RespValue> = COMMAND_TABLE
838 .iter()
839 .map(|cmd| RespValue::Array(Some(cmd.to_resp_array())))
840 .collect();
841 RespValue::Array(Some(commands))
842 } else {
843 let mut results = Vec::new();
844 for arg in args {
845 let cmd_name = String::from_utf8_lossy(&arg);
846 if let Some(cmd_def) = CommandDef::find(&cmd_name) {
847 results.push(RespValue::Array(Some(cmd_def.to_resp_array())));
848 } else {
849 results.push(RespValue::BulkString(None));
850 }
851 }
852 RespValue::Array(Some(results))
853 }
854 }
855 "DOCS" => RespValue::Array(Some(vec![])),
856 "LIST" => {
857 let names: Vec<RespValue> = COMMAND_TABLE
858 .iter()
859 .map(|cmd| RespValue::BulkString(Some(Bytes::from(cmd.name))))
860 .collect();
861 RespValue::Array(Some(names))
862 }
863 "HELP" => {
864 let help_text = vec![
865 "COMMAND",
866 "COMMAND COUNT",
867 "COMMAND INFO <command-name> [<command-name> ...]",
868 "COMMAND LIST",
869 "COMMAND DOCS [<command-name> ...]",
870 ];
871 RespValue::Array(Some(
872 help_text
873 .into_iter()
874 .map(|s| RespValue::BulkString(Some(Bytes::from(s))))
875 .collect(),
876 ))
877 }
878 _ => RespValue::Error(format!(
879 "ERR Unknown subcommand '{}'. Try COMMAND HELP.",
880 subcmd
881 )),
882 },
883 }
884 }
885}