Skip to main content

rust_cache_server/
command.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4use crate::protocol::RespFrame;
5use crate::store::Store;
6
7pub async fn handle_command(store: Arc<Store>, frame: RespFrame) -> RespFrame {
8    let RespFrame::Array(Some(items)) = frame else {
9        return RespFrame::Error("ERR protocol error".into());
10    };
11
12    if items.is_empty() {
13        return RespFrame::Error("ERR empty command".into());
14    }
15
16    let cmd_name = match &items[0] {
17        RespFrame::BulkString(Some(b)) => String::from_utf8_lossy(b).to_ascii_uppercase(),
18        RespFrame::SimpleString(s) => s.to_ascii_uppercase(),
19        _ => return RespFrame::Error("ERR invalid command".into()),
20    };
21
22    match cmd_name.as_str() {
23        "PING" => RespFrame::SimpleString("PONG".into()),
24
25        "INFO" => {
26            // Minimal INFO response so that ioredis is happy.
27            // Redis INFO is a bulk string with lines "key:value\r\n".
28            let info = "redis_version:rust-cache-0.1.0\r\nrole:master\r\n";
29            RespFrame::BulkString(Some(info.as_bytes().to_vec()))
30        }
31
32        "GET" => {
33            if items.len() != 2 {
34                return RespFrame::Error("ERR wrong number of arguments for 'get' command".into());
35            }
36            let key = bulk_to_string(&items[1]);
37            match key {
38                None => RespFrame::Error("ERR invalid key".into()),
39                Some(k) => match store.get(&k) {
40                    Some(v) => RespFrame::BulkString(Some(v)),
41                    None => RespFrame::BulkString(None),
42                },
43            }
44        }
45
46        "SET" => {
47            if items.len() < 3 {
48                return RespFrame::Error("ERR wrong number of arguments for 'set' command".into());
49            }
50            let key = match bulk_to_string(&items[1]) {
51                Some(k) => k,
52                None => return RespFrame::Error("ERR invalid key".into()),
53            };
54            let val = match &items[2] {
55                RespFrame::BulkString(Some(b)) => b.clone(),
56                RespFrame::SimpleString(s) => s.clone().into_bytes(),
57                _ => return RespFrame::Error("ERR invalid value".into()),
58            };
59
60            // Optional EX seconds
61            let mut ttl: Option<Duration> = None;
62            if items.len() >= 5 {
63                if let RespFrame::BulkString(Some(flag)) = &items[3] {
64                    if flag.eq_ignore_ascii_case(b"EX") {
65                        if let RespFrame::BulkString(Some(sec_bytes)) = &items[4] {
66                            if let Ok(sec_str) = std::str::from_utf8(sec_bytes) {
67                                if let Ok(sec) = sec_str.parse::<u64>() {
68                                    ttl = Some(Duration::from_secs(sec));
69                                }
70                            }
71                        }
72                    }
73                }
74            }
75
76            match store.set(key, val, ttl) {
77                Ok(()) => RespFrame::SimpleString("OK".into()),
78                Err(()) => RespFrame::Error("ERR memory limit reached".into()),
79            }
80        }
81
82        "DEL" => {
83            if items.len() < 2 {
84                return RespFrame::Error("ERR wrong number of arguments for 'del' command".into());
85            }
86            let mut keys = Vec::with_capacity(items.len() - 1);
87            for it in &items[1..] {
88                if let Some(k) = bulk_to_string(it) {
89                    keys.push(k);
90                }
91            }
92            let removed = store.del(&keys);
93            RespFrame::Integer(removed as i64)
94        }
95
96        "EXPIRE" => {
97            if items.len() != 3 {
98                return RespFrame::Error(
99                    "ERR wrong number of arguments for 'expire' command".into(),
100                );
101            }
102            let key = match bulk_to_string(&items[1]) {
103                Some(k) => k,
104                None => return RespFrame::Error("ERR invalid key".into()),
105            };
106            let seconds = match &items[2] {
107                RespFrame::BulkString(Some(b)) => match std::str::from_utf8(b) {
108                    Ok(s) => match s.parse::<u64>() {
109                        Ok(v) => v,
110                        Err(_) => return RespFrame::Error("ERR value is not an integer".into()),
111                    },
112                    Err(_) => return RespFrame::Error("ERR value is not an integer".into()),
113                },
114                _ => return RespFrame::Error("ERR value is not an integer".into()),
115            };
116
117            let ok = store.expire(&key, Duration::from_secs(seconds));
118            RespFrame::Integer(if ok { 1 } else { 0 })
119        }
120
121        "TTL" => {
122            if items.len() != 2 {
123                return RespFrame::Error("ERR wrong number of arguments for 'ttl' command".into());
124            }
125            let key = match bulk_to_string(&items[1]) {
126                Some(k) => k,
127                None => return RespFrame::Error("ERR invalid key".into()),
128            };
129            match store.ttl(&key) {
130                Some(d) => RespFrame::Integer(d.as_secs() as i64),
131                None => RespFrame::Integer(-2),
132            }
133        }
134
135        "KEYS" => {
136            if items.len() != 2 {
137                return RespFrame::Error("ERR wrong number of arguments for 'keys' command".into());
138            }
139            let pattern = match bulk_to_string(&items[1]) {
140                Some(p) => p,
141                None => return RespFrame::Error("ERR invalid pattern".into()),
142            };
143
144            let all_keys = store.keys();
145            let matched: Vec<RespFrame> = all_keys
146                .into_iter()
147                .filter(|k| pattern_match(&pattern, k))
148                .map(|k| RespFrame::BulkString(Some(k.into_bytes())))
149                .collect();
150
151            RespFrame::Array(Some(matched))
152        }
153
154        "FLUSHALL" | "CLEARALL" => {
155            if items.len() != 1 {
156                return RespFrame::Error(
157                    "ERR wrong number of arguments for 'flushall' command".into(),
158                );
159            }
160            store.clear_all();
161            RespFrame::SimpleString("OK".into())
162        }
163
164        "INCR" => {
165            if items.len() != 2 {
166                return RespFrame::Error("ERR wrong number of arguments for 'incr' command".into());
167            }
168            let key = match bulk_to_string(&items[1]) {
169                Some(k) => k,
170                None => return RespFrame::Error("ERR invalid key".into()),
171            };
172            match store.incr_by(&key, 1, true) {
173                Some(val) => RespFrame::Integer(val),
174                None => RespFrame::Error("ERR value is not an integer or out of range".into()),
175            }
176        }
177
178        "DECR" => {
179            if items.len() != 2 {
180                return RespFrame::Error("ERR wrong number of arguments for 'decr' command".into());
181            }
182            let key = match bulk_to_string(&items[1]) {
183                Some(k) => k,
184                None => return RespFrame::Error("ERR invalid key".into()),
185            };
186            match store.incr_by(&key, -1, true) {
187                Some(val) => RespFrame::Integer(val),
188                None => RespFrame::Error("ERR value is not an integer or out of range".into()),
189            }
190        }
191
192        "INCRBY" => {
193            if items.len() != 3 {
194                return RespFrame::Error(
195                    "ERR wrong number of arguments for 'incrby' command".into(),
196                );
197            }
198            let key = match bulk_to_string(&items[1]) {
199                Some(k) => k,
200                None => return RespFrame::Error("ERR invalid key".into()),
201            };
202            let increment = match &items[2] {
203                RespFrame::BulkString(Some(b)) => match std::str::from_utf8(b) {
204                    Ok(s) => match s.parse::<i64>() {
205                        Ok(v) => v,
206                        Err(_) => {
207                            return RespFrame::Error("ERR value is not an integer".into());
208                        }
209                    },
210                    Err(_) => return RespFrame::Error("ERR value is not an integer".into()),
211                },
212                _ => return RespFrame::Error("ERR value is not an integer".into()),
213            };
214            match store.incr_by(&key, increment, true) {
215                Some(val) => RespFrame::Integer(val),
216                None => RespFrame::Error("ERR value is not an integer or out of range".into()),
217            }
218        }
219
220        "DECRBY" => {
221            if items.len() != 3 {
222                return RespFrame::Error(
223                    "ERR wrong number of arguments for 'decrby' command".into(),
224                );
225            }
226            let key = match bulk_to_string(&items[1]) {
227                Some(k) => k,
228                None => return RespFrame::Error("ERR invalid key".into()),
229            };
230            let decrement = match &items[2] {
231                RespFrame::BulkString(Some(b)) => match std::str::from_utf8(b) {
232                    Ok(s) => match s.parse::<i64>() {
233                        Ok(v) => v,
234                        Err(_) => {
235                            return RespFrame::Error("ERR value is not an integer".into());
236                        }
237                    },
238                    Err(_) => return RespFrame::Error("ERR value is not an integer".into()),
239                },
240                _ => return RespFrame::Error("ERR value is not an integer".into()),
241            };
242            match store.incr_by(&key, -decrement, true) {
243                Some(val) => RespFrame::Integer(val),
244                None => RespFrame::Error("ERR value is not an integer or out of range".into()),
245            }
246        }
247
248        _ => RespFrame::Error(format!("ERR unknown command '{cmd_name}'")),
249    }
250}
251
252fn bulk_to_string(frame: &RespFrame) -> Option<String> {
253    match frame {
254        RespFrame::BulkString(Some(b)) => String::from_utf8(b.clone()).ok(),
255        RespFrame::SimpleString(s) => Some(s.clone()),
256        _ => None,
257    }
258}
259
260fn pattern_match(pattern: &str, text: &str) -> bool {
261    // Very small glob subset: only '*' wildcard, like Redis KEYS.
262    if pattern == "*" {
263        return true;
264    }
265
266    let mut p_iter = pattern.chars().peekable();
267    let mut t_iter = text.chars().peekable();
268
269    let mut star: Option<(usize, usize)> = None;
270    let pattern_chars: Vec<char> = pattern.chars().collect();
271    let text_chars: Vec<char> = text.chars().collect();
272    let (mut i, mut j) = (0usize, 0usize);
273
274    while j < text_chars.len() {
275        if i < pattern_chars.len()
276            && (pattern_chars[i] == '?' || pattern_chars[i] == text_chars[j])
277        {
278            i += 1;
279            j += 1;
280        } else if i < pattern_chars.len() && pattern_chars[i] == '*' {
281            star = Some((i, j));
282            i += 1;
283        } else if let Some((si, sj)) = star {
284            i = si + 1;
285            j = sj + 1;
286            star = Some((si, j));
287        } else {
288            return false;
289        }
290    }
291
292    while i < pattern_chars.len() && pattern_chars[i] == '*' {
293        i += 1;
294    }
295
296    i == pattern_chars.len()
297}
298
299