1use super::Command;
2use crate::protocol::resp::RespValue;
3use bytes::Bytes;
4use feoxdb::FeoxStore;
5use std::sync::Arc;
6
7fn match_pattern(key: &[u8], pattern: &str) -> bool {
9 let key_str = String::from_utf8_lossy(key);
10 glob_match(pattern, &key_str)
11}
12
13fn glob_match(pattern: &str, text: &str) -> bool {
15 let mut p_idx = 0;
16 let mut t_idx = 0;
17 let mut star_idx = None;
18 let mut star_match = None;
19
20 let pattern_bytes = pattern.as_bytes();
21 let text_bytes = text.as_bytes();
22
23 while t_idx < text_bytes.len() {
24 if p_idx < pattern_bytes.len() {
25 match pattern_bytes[p_idx] {
26 b'*' => {
27 star_idx = Some(p_idx);
28 star_match = Some(t_idx);
29 p_idx += 1;
30 }
31 b'?' => {
32 p_idx += 1;
33 t_idx += 1;
34 }
35 _ => {
36 if pattern_bytes[p_idx] == text_bytes[t_idx] {
37 p_idx += 1;
38 t_idx += 1;
39 } else if let Some(star) = star_idx {
40 p_idx = star + 1;
41 star_match = Some(star_match.unwrap() + 1);
42 t_idx = star_match.unwrap();
43 } else {
44 return false;
45 }
46 }
47 }
48 } else if let Some(star) = star_idx {
49 p_idx = star + 1;
50 star_match = Some(star_match.unwrap() + 1);
51 t_idx = star_match.unwrap();
52 } else {
53 return false;
54 }
55 }
56
57 while p_idx < pattern_bytes.len() && pattern_bytes[p_idx] == b'*' {
59 p_idx += 1;
60 }
61
62 p_idx == pattern_bytes.len()
63}
64
65fn extract_prefix(pattern: &str) -> &str {
67 for (i, ch) in pattern.char_indices() {
68 if ch == '*' || ch == '?' || ch == '[' {
69 return &pattern[..i];
70 }
71 }
72 pattern
73}
74
75pub struct CommandExecutor {
79 store: Arc<FeoxStore>,
80 start_time: std::time::Instant,
81 commands_processed: Arc<std::sync::atomic::AtomicU64>,
82}
83
84impl CommandExecutor {
85 pub fn new(store: Arc<FeoxStore>) -> Self {
87 Self {
88 store,
89 start_time: std::time::Instant::now(),
90 commands_processed: Arc::new(std::sync::atomic::AtomicU64::new(0)),
91 }
92 }
93
94 #[inline]
96 pub fn execute(&self, cmd: Command) -> RespValue {
97 use std::sync::atomic::Ordering;
98
99 self.commands_processed.fetch_add(1, Ordering::Relaxed);
101
102 match cmd {
103 Command::Get(key) => match self.store.get_bytes(&key) {
104 Ok(value) => RespValue::BulkString(Some(value)),
105 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::BulkString(None),
106 Err(e) => RespValue::Error(format!("ERR {}", e)),
107 },
108
109 Command::Set { key, value, ex, px } => {
110 let result = if let Some(seconds) = ex {
112 self.store
113 .insert_with_ttl_and_timestamp(&key, &value, seconds, None)
114 } else if let Some(millis) = px {
115 self.store
116 .insert_with_ttl_and_timestamp(&key, &value, millis / 1000, None)
117 } else {
118 self.store.insert_with_timestamp(&key, &value, None)
119 };
120
121 match result {
122 Ok(_) => RespValue::SimpleString(Bytes::from_static(b"OK")),
123 Err(e) => RespValue::Error(format!("ERR {}", e)),
124 }
125 }
126
127 Command::Del(keys) => {
128 let mut count = 0i64;
129 for key in keys {
130 if self.store.delete(&key).is_ok() {
131 count += 1;
132 }
133 }
134 RespValue::Integer(count)
135 }
136
137 Command::Exists(keys) => {
138 let count = keys
139 .iter()
140 .filter(|key| self.store.contains_key(key))
141 .count() as i64;
142 RespValue::Integer(count)
143 }
144
145 Command::Incr(key) => match self.store.atomic_increment(&key, 1) {
146 Ok(val) => RespValue::Integer(val),
147 Err(e) => RespValue::Error(format!("ERR {}", e)),
148 },
149
150 Command::IncrBy { key, delta } => match self.store.atomic_increment(&key, delta) {
151 Ok(val) => RespValue::Integer(val),
152 Err(e) => RespValue::Error(format!("ERR {}", e)),
153 },
154
155 Command::Decr(key) => match self.store.atomic_increment(&key, -1) {
156 Ok(val) => RespValue::Integer(val),
157 Err(e) => RespValue::Error(format!("ERR {}", e)),
158 },
159
160 Command::DecrBy { key, delta } => match self.store.atomic_increment(&key, -delta) {
161 Ok(val) => RespValue::Integer(val),
162 Err(e) => RespValue::Error(format!("ERR {}", e)),
163 },
164
165 Command::Expire { key, seconds } => match self.store.update_ttl(&key, seconds) {
166 Ok(_) => RespValue::Integer(1),
167 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(0),
168 Err(e) => RespValue::Error(format!("ERR {}", e)),
169 },
170
171 Command::PExpire { key, milliseconds } => {
172 match self.store.update_ttl(&key, milliseconds / 1000) {
173 Ok(_) => RespValue::Integer(1),
174 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(0),
175 Err(e) => RespValue::Error(format!("ERR {}", e)),
176 }
177 }
178
179 Command::Ttl(key) => {
180 match self.store.get_ttl(&key) {
181 Ok(Some(ttl)) => RespValue::Integer(ttl as i64),
182 Ok(None) => RespValue::Integer(-1), Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(-2),
184 Err(e) => RespValue::Error(format!("ERR {}", e)),
185 }
186 }
187
188 Command::PTtl(key) => {
189 match self.store.get_ttl(&key) {
190 Ok(Some(ttl)) => RespValue::Integer((ttl * 1000) as i64),
191 Ok(None) => RespValue::Integer(-1), Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(-2),
193 Err(e) => RespValue::Error(format!("ERR {}", e)),
194 }
195 }
196
197 Command::Persist(key) => match self.store.persist(&key) {
198 Ok(_) => RespValue::Integer(1),
199 Err(feoxdb::FeoxError::KeyNotFound) => RespValue::Integer(0),
200 Err(e) => RespValue::Error(format!("ERR {}", e)),
201 },
202
203 Command::MGet(keys) => {
204 let values: Vec<RespValue> = keys
205 .into_iter()
206 .map(|key| match self.store.get_bytes(&key) {
207 Ok(value) => RespValue::BulkString(Some(value)),
208 Err(_) => RespValue::BulkString(None),
209 })
210 .collect();
211 RespValue::Array(Some(values))
212 }
213
214 Command::MSet(pairs) => {
215 for (key, value) in pairs {
216 if let Err(e) = self.store.insert_with_timestamp(&key, &value, None) {
218 return RespValue::Error(format!("ERR {}", e));
219 }
220 }
221 RespValue::SimpleString(Bytes::from_static(b"OK"))
222 }
223
224 Command::Ping(msg) => match msg {
225 Some(m) => RespValue::BulkString(Some(m)),
226 None => RespValue::SimpleString(Bytes::from_static(b"PONG")),
227 },
228
229 Command::Echo(msg) => RespValue::BulkString(Some(msg)),
230
231 Command::Config { action, args } => {
232 match action.to_uppercase().as_str() {
233 "GET" => {
234 if args.is_empty() {
236 RespValue::Array(Some(vec![]))
237 } else {
238 let mut results = Vec::new();
240 for arg in args {
241 results.push(RespValue::BulkString(Some(arg)));
242 results.push(RespValue::BulkString(None)); }
244 RespValue::Array(Some(results))
245 }
246 }
247 "SET" => {
248 RespValue::SimpleString(Bytes::from_static(b"OK"))
250 }
251 _ => RespValue::Error(format!("ERR Unknown CONFIG subcommand '{}'", action)),
252 }
253 }
254
255 Command::Command => {
256 let commands = vec![
259 vec![
261 RespValue::BulkString(Some(Bytes::from_static(b"GET"))),
262 RespValue::Integer(2), RespValue::Array(Some(vec![RespValue::BulkString(Some(
264 Bytes::from_static(b"readonly"),
265 ))])),
266 RespValue::Integer(1), RespValue::Integer(1), RespValue::Integer(1), ],
270 vec![
271 RespValue::BulkString(Some(Bytes::from_static(b"SET"))),
272 RespValue::Integer(-3), RespValue::Array(Some(vec![RespValue::BulkString(Some(
274 Bytes::from_static(b"write"),
275 ))])),
276 RespValue::Integer(1),
277 RespValue::Integer(1),
278 RespValue::Integer(1),
279 ],
280 vec![
281 RespValue::BulkString(Some(Bytes::from_static(b"DEL"))),
282 RespValue::Integer(-2), RespValue::Array(Some(vec![RespValue::BulkString(Some(
284 Bytes::from_static(b"write"),
285 ))])),
286 RespValue::Integer(1),
287 RespValue::Integer(-1), RespValue::Integer(1),
289 ],
290 vec![
291 RespValue::BulkString(Some(Bytes::from_static(b"EXISTS"))),
292 RespValue::Integer(-2),
293 RespValue::Array(Some(vec![RespValue::BulkString(Some(
294 Bytes::from_static(b"readonly"),
295 ))])),
296 RespValue::Integer(1),
297 RespValue::Integer(-1),
298 RespValue::Integer(1),
299 ],
300 vec![
302 RespValue::BulkString(Some(Bytes::from_static(b"INCR"))),
303 RespValue::Integer(2),
304 RespValue::Array(Some(vec![RespValue::BulkString(Some(
305 Bytes::from_static(b"write"),
306 ))])),
307 RespValue::Integer(1),
308 RespValue::Integer(1),
309 RespValue::Integer(1),
310 ],
311 vec![
312 RespValue::BulkString(Some(Bytes::from_static(b"DECR"))),
313 RespValue::Integer(2),
314 RespValue::Array(Some(vec![RespValue::BulkString(Some(
315 Bytes::from_static(b"write"),
316 ))])),
317 RespValue::Integer(1),
318 RespValue::Integer(1),
319 RespValue::Integer(1),
320 ],
321 vec![
323 RespValue::BulkString(Some(Bytes::from_static(b"EXPIRE"))),
324 RespValue::Integer(3),
325 RespValue::Array(Some(vec![RespValue::BulkString(Some(
326 Bytes::from_static(b"write"),
327 ))])),
328 RespValue::Integer(1),
329 RespValue::Integer(1),
330 RespValue::Integer(1),
331 ],
332 vec![
333 RespValue::BulkString(Some(Bytes::from_static(b"TTL"))),
334 RespValue::Integer(2),
335 RespValue::Array(Some(vec![RespValue::BulkString(Some(
336 Bytes::from_static(b"readonly"),
337 ))])),
338 RespValue::Integer(1),
339 RespValue::Integer(1),
340 RespValue::Integer(1),
341 ],
342 vec![
344 RespValue::BulkString(Some(Bytes::from_static(b"MGET"))),
345 RespValue::Integer(-2),
346 RespValue::Array(Some(vec![RespValue::BulkString(Some(
347 Bytes::from_static(b"readonly"),
348 ))])),
349 RespValue::Integer(1),
350 RespValue::Integer(-1),
351 RespValue::Integer(1),
352 ],
353 vec![
354 RespValue::BulkString(Some(Bytes::from_static(b"MSET"))),
355 RespValue::Integer(-3),
356 RespValue::Array(Some(vec![RespValue::BulkString(Some(
357 Bytes::from_static(b"write"),
358 ))])),
359 RespValue::Integer(1),
360 RespValue::Integer(-1),
361 RespValue::Integer(2), ],
363 vec![
365 RespValue::BulkString(Some(Bytes::from_static(b"PING"))),
366 RespValue::Integer(-1),
367 RespValue::Array(Some(vec![RespValue::BulkString(Some(
368 Bytes::from_static(b"fast"),
369 ))])),
370 RespValue::Integer(0),
371 RespValue::Integer(0),
372 RespValue::Integer(0),
373 ],
374 vec![
376 RespValue::BulkString(Some(Bytes::from_static(b"JSONPATCH"))),
377 RespValue::Integer(3),
378 RespValue::Array(Some(vec![RespValue::BulkString(Some(
379 Bytes::from_static(b"write"),
380 ))])),
381 RespValue::Integer(1),
382 RespValue::Integer(1),
383 RespValue::Integer(1),
384 ],
385 vec![
386 RespValue::BulkString(Some(Bytes::from_static(b"CAS"))),
387 RespValue::Integer(4),
388 RespValue::Array(Some(vec![RespValue::BulkString(Some(
389 Bytes::from_static(b"write"),
390 ))])),
391 RespValue::Integer(1),
392 RespValue::Integer(1),
393 RespValue::Integer(1),
394 ],
395 ];
396
397 RespValue::Array(Some(
398 commands
399 .into_iter()
400 .map(|cmd| RespValue::Array(Some(cmd)))
401 .collect(),
402 ))
403 }
404
405 Command::Quit => RespValue::SimpleString(Bytes::from_static(b"OK")),
406
407 Command::FlushDb => {
408 RespValue::Error("ERR FLUSHDB requires server restart. For persistent mode, also delete data files.".to_string())
413 }
414
415 Command::Keys(pattern) => {
416 let prefix = extract_prefix(&pattern);
418
419 let (start_key, end_key) = if prefix.is_empty() {
421 (vec![], vec![0xFF; 255])
423 } else if pattern == prefix {
424 return match self.store.get_bytes(prefix.as_bytes()) {
426 Ok(_) => {
427 let keys =
428 vec![RespValue::BulkString(Some(Bytes::from(prefix.to_string())))];
429 RespValue::Array(Some(keys))
430 }
431 Err(_) => RespValue::Array(Some(vec![])),
432 };
433 } else {
434 let mut end = prefix.as_bytes().to_vec();
436 end.push(b'~'); (prefix.as_bytes().to_vec(), end)
438 };
439
440 match self.store.range_query(&start_key, &end_key, 100000) {
442 Ok(pairs) => {
443 let keys: Vec<RespValue> = pairs
444 .into_iter()
445 .filter(|(key, _)| match_pattern(key, &pattern))
446 .map(|(key, _)| RespValue::BulkString(Some(Bytes::from(key))))
447 .collect();
448 RespValue::Array(Some(keys))
449 }
450 Err(e) => RespValue::Error(format!("ERR {}", e)),
451 }
452 }
453
454 Command::Scan {
455 cursor,
456 count,
457 pattern,
458 } => {
459 let start_key = if cursor.is_empty() || cursor == b"0" {
461 vec![]
462 } else {
463 cursor.clone()
464 };
465
466 let (scan_start, scan_end) = if let Some(ref pat) = pattern {
468 let prefix = extract_prefix(pat);
469 if !prefix.is_empty() && pat.starts_with(prefix) && pat.contains('*') {
470 let mut end = prefix.as_bytes().to_vec();
472 end.push(b'~');
473
474 let actual_start = if start_key.len() > prefix.len()
476 && start_key.starts_with(prefix.as_bytes())
477 {
478 start_key
479 } else if start_key.is_empty() {
480 prefix.as_bytes().to_vec()
481 } else {
482 start_key
483 };
484
485 (actual_start, end)
486 } else {
487 (start_key, vec![0xFF; 255])
488 }
489 } else {
490 (start_key, vec![0xFF; 255])
491 };
492
493 let fetch_count = if pattern.is_some() { count * 2 } else { count };
495 match self
496 .store
497 .range_query(&scan_start, &scan_end, fetch_count + 1)
498 {
499 Ok(pairs) => {
500 let mut keys = Vec::new();
501 let mut next_cursor = None;
502
503 for (key, _) in pairs.into_iter() {
504 if keys.len() >= count {
506 next_cursor = Some(key.clone());
507 break;
508 }
509
510 if let Some(ref pat) = pattern {
512 if !match_pattern(&key, pat) {
513 continue;
514 }
515 }
516
517 keys.push(RespValue::BulkString(Some(Bytes::from(key.clone()))));
518 }
519
520 let cursor_str = if let Some(next) = next_cursor {
522 Bytes::from(next)
523 } else {
524 Bytes::from_static(b"0") };
526
527 RespValue::Array(Some(vec![
528 RespValue::BulkString(Some(cursor_str)),
529 RespValue::Array(Some(keys)),
530 ]))
531 }
532 Err(e) => RespValue::Error(format!("ERR {}", e)),
533 }
534 }
535
536 Command::Info(section) => {
537 use std::sync::atomic::Ordering;
538
539 let uptime = self.start_time.elapsed().as_secs();
541 let commands = self.commands_processed.load(Ordering::Relaxed);
542 let stats = self.store.stats();
543
544 let format_bytes = |bytes: usize| -> String {
546 if bytes < 1024 {
547 format!("{}B", bytes)
548 } else if bytes < 1024 * 1024 {
549 format!("{:.1}K", bytes as f64 / 1024.0)
550 } else if bytes < 1024 * 1024 * 1024 {
551 format!("{:.1}M", bytes as f64 / (1024.0 * 1024.0))
552 } else {
553 format!("{:.1}G", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
554 }
555 };
556
557 let mut info = String::new();
558
559 if section.is_none()
561 || section
562 .as_ref()
563 .map(|s| s.eq_ignore_ascii_case("server"))
564 .unwrap_or(false)
565 {
566 info.push_str(&format!(
567 "# Server\r\n\
568 redis_version:feox-{}\r\n\
569 redis_mode:standalone\r\n\
570 process_id:{}\r\n\
571 tcp_port:6379\r\n\
572 uptime_in_seconds:{}\r\n",
573 env!("CARGO_PKG_VERSION"),
574 std::process::id(),
575 uptime
576 ));
577 }
578
579 if section.is_none()
581 || section
582 .as_ref()
583 .map(|s| s.eq_ignore_ascii_case("memory"))
584 .unwrap_or(false)
585 {
586 info.push_str(&format!(
587 "# Memory\r\n\
588 used_memory:{}\r\n\
589 used_memory_human:{}\r\n\
590 used_memory_cache:{}\r\n\
591 used_memory_cache_human:{}\r\n",
592 stats.memory_usage,
593 format_bytes(stats.memory_usage),
594 stats.cache_memory,
595 format_bytes(stats.cache_memory)
596 ));
597 }
598
599 if section.is_none()
601 || section
602 .as_ref()
603 .map(|s| s.eq_ignore_ascii_case("stats"))
604 .unwrap_or(false)
605 {
606 info.push_str(&format!(
607 "# Stats\r\n\
608 total_connections_received:0\r\n\
609 total_commands_processed:{}\r\n\
610 instantaneous_ops_per_sec:0\r\n\
611 total_net_input_bytes:0\r\n\
612 total_net_output_bytes:0\r\n\
613 total_operations:{}\r\n\
614 total_gets:{}\r\n\
615 total_inserts:{}\r\n\
616 keyspace_hits:{}\r\n\
617 keyspace_misses:{}\r\n\
618 cache_hit_rate:{:.2}\r\n",
619 commands,
620 stats.total_operations,
621 stats.total_gets,
622 stats.total_inserts,
623 stats.cache_hits,
624 stats.cache_misses,
625 stats.cache_hit_rate * 100.0
626 ));
627 }
628
629 if section.is_none()
631 || section
632 .as_ref()
633 .map(|s| s.eq_ignore_ascii_case("keyspace"))
634 .unwrap_or(false)
635 {
636 info.push_str(&format!(
637 "# Keyspace\r\n\
638 db0:keys={},expires=0,avg_ttl=0\r\n",
639 stats.record_count
640 ));
641 }
642
643 RespValue::BulkString(Some(Bytes::from(info)))
644 }
645
646 Command::JsonPatch { key, patch } => {
647 match self.store.json_patch(&key, &patch) {
649 Ok(_) => RespValue::SimpleString(Bytes::from_static(b"OK")),
650 Err(e) => RespValue::Error(format!("ERR {}", e)),
651 }
652 }
653
654 Command::Cas {
655 key,
656 expected,
657 new_value,
658 } => {
659 match self.store.compare_and_swap(&key, &expected, &new_value) {
661 Ok(swapped) => RespValue::Integer(if swapped { 1 } else { 0 }),
662 Err(e) => RespValue::Error(format!("ERR {}", e)),
663 }
664 }
665 }
666 }
667}