Skip to content

Commit 76a9d28

Browse files
committed
Exists command
Also remove intermediate step in try_from parser to command
1 parent 6531dee commit 76a9d28

File tree

3 files changed

+122
-38
lines changed

3 files changed

+122
-38
lines changed

src/commands/exists.rs

+92-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::sync::{Arc, Mutex};
22

33
use crate::commands::executable::Executable;
44
use crate::commands::CommandParser;
5+
use crate::commands::CommandParserError;
56
use crate::frame::Frame;
67
use crate::store::Store;
78
use crate::Error;
@@ -12,17 +13,103 @@ pub struct Exists {
1213
}
1314

1415
impl Executable for Exists {
15-
fn exec(self, _store: Arc<Mutex<Store>>) -> Result<Frame, Error> {
16-
Ok(Frame::Simple("OK".to_string()))
16+
fn exec(self, store: Arc<Mutex<Store>>) -> Result<Frame, Error> {
17+
let mut count = 0;
18+
let store = store.lock().unwrap();
19+
for key in self.keys {
20+
if store.exists(&key) {
21+
count += 1;
22+
}
23+
}
24+
Ok(Frame::Integer(count))
1725
}
1826
}
1927

2028
impl TryFrom<&mut CommandParser> for Exists {
2129
type Error = Error;
2230

2331
fn try_from(parser: &mut CommandParser) -> Result<Self, Self::Error> {
24-
let key = parser.next_string()?;
25-
println!("key: {}", key);
26-
Ok(Self { keys: vec![key] })
32+
let mut keys = vec![];
33+
34+
loop {
35+
match parser.next_string() {
36+
Ok(key) => keys.push(key),
37+
Err(CommandParserError::EndOfStream) if !keys.is_empty() => {
38+
break;
39+
}
40+
Err(err) => return Err(err.into()),
41+
}
42+
}
43+
44+
Ok(Self { keys })
45+
}
46+
}
47+
48+
#[cfg(test)]
49+
mod tests {
50+
use bytes::Bytes;
51+
52+
use crate::commands::Command;
53+
54+
use super::*;
55+
56+
#[test]
57+
fn multiple_keys() {
58+
let frame = Frame::Array(vec![
59+
Frame::Bulk(Bytes::from("EXISTS")),
60+
Frame::Bulk(Bytes::from("foo")),
61+
Frame::Bulk(Bytes::from("bar")),
62+
Frame::Bulk(Bytes::from("baz")),
63+
]);
64+
let cmd = Command::try_from(frame).unwrap();
65+
assert_eq!(
66+
cmd,
67+
Command::Exists(Exists {
68+
keys: vec!["foo".to_string(), "bar".to_string(), "baz".to_string()]
69+
})
70+
);
71+
}
72+
73+
#[test]
74+
fn single_key() {
75+
let frame = Frame::Array(vec![
76+
Frame::Bulk(Bytes::from("EXISTS")),
77+
Frame::Bulk(Bytes::from("foo")),
78+
]);
79+
let cmd = Command::try_from(frame).unwrap();
80+
assert_eq!(
81+
cmd,
82+
Command::Exists(Exists {
83+
keys: vec!["foo".to_string()]
84+
})
85+
);
86+
}
87+
88+
#[test]
89+
fn zero_keys() {
90+
let frame = Frame::Array(vec![Frame::Bulk(Bytes::from("EXISTS"))]);
91+
let err = Command::try_from(frame).err().unwrap();
92+
let err = err.downcast_ref::<CommandParserError>().unwrap();
93+
94+
assert_eq!(*err, CommandParserError::EndOfStream);
95+
}
96+
97+
#[test]
98+
fn invalid_frame() {
99+
let frame = Frame::Array(vec![
100+
Frame::Bulk(Bytes::from("EXISTS")),
101+
Frame::Integer(42),
102+
Frame::Bulk(Bytes::from("foo")),
103+
]);
104+
let err = Command::try_from(frame).err().unwrap();
105+
let err = err.downcast_ref::<CommandParserError>().unwrap();
106+
107+
assert_eq!(
108+
*err,
109+
CommandParserError::InvalidFrame {
110+
expected: "simple or bulk string".to_string(),
111+
actual: Frame::Integer(42)
112+
}
113+
);
27114
}
28115
}

src/commands/mod.rs

+26-33
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,34 @@ impl TryFrom<Frame> for Command {
6666
type Error = Error;
6767

6868
fn try_from(frame: Frame) -> Result<Self, Self::Error> {
69-
let mut parser = CommandParser::try_from(frame)?;
69+
// Clients send commands to the Redis server as RESP arrays.
70+
let frames = match frame {
71+
Frame::Array(array) => array,
72+
frame => {
73+
return Err(Box::new(CommandParserError::InvalidFrame {
74+
expected: "array".to_string(),
75+
actual: frame,
76+
}))
77+
}
78+
};
79+
80+
let parser = &mut CommandParser {
81+
parts: frames.into_iter(),
82+
};
83+
7084
let command_name = parser.parse_command_name()?;
7185

7286
match &command_name[..] {
73-
"get" => Get::try_from(&mut parser).map(Command::Get),
74-
"set" => Set::try_from(&mut parser).map(Command::Set),
75-
"exists" => Exists::try_from(&mut parser).map(Command::Exists),
76-
"dbsize" => DBSize::try_from(&mut parser).map(Command::DBsize),
77-
"info" => Info::try_from(&mut parser).map(Command::Info),
78-
"client" => Client::try_from(&mut parser).map(Command::Client),
79-
"module" => Module::try_from(&mut parser).map(Command::Module),
80-
"command" => Foo::try_from(&mut parser).map(Command::Command),
81-
"config" => Config::try_from(&mut parser).map(Command::Config),
82-
"type" => Type::try_from(&mut parser).map(Command::Type),
87+
"get" => Get::try_from(parser).map(Command::Get),
88+
"set" => Set::try_from(parser).map(Command::Set),
89+
"exists" => Exists::try_from(parser).map(Command::Exists),
90+
"dbsize" => DBSize::try_from(parser).map(Command::DBsize),
91+
"info" => Info::try_from(parser).map(Command::Info),
92+
"client" => Client::try_from(parser).map(Command::Client),
93+
"module" => Module::try_from(parser).map(Command::Module),
94+
"command" => Foo::try_from(parser).map(Command::Command),
95+
"config" => Config::try_from(parser).map(Command::Config),
96+
"type" => Type::try_from(parser).map(Command::Type),
8397
name => Err(format!("protocol error; unknown command {:?}", name).into()),
8498
}
8599
}
@@ -147,7 +161,7 @@ impl CommandParser {
147161
}
148162
}
149163

150-
#[derive(Debug, ThisError)]
164+
#[derive(Debug, ThisError, PartialEq)]
151165
pub(crate) enum CommandParserError {
152166
#[error("protocol error; invalid frame, expected {expected}, got {actual}")]
153167
InvalidFrame { expected: String, actual: Frame },
@@ -157,27 +171,6 @@ pub(crate) enum CommandParserError {
157171
EndOfStream,
158172
}
159173

160-
impl TryFrom<Frame> for CommandParser {
161-
type Error = CommandParserError;
162-
163-
fn try_from(frame: Frame) -> Result<Self, Self::Error> {
164-
// Clients send commands to the Redis server as RESP arrays.
165-
let frames = match frame {
166-
Frame::Array(array) => array,
167-
frame => {
168-
return Err(CommandParserError::InvalidFrame {
169-
expected: "array".to_string(),
170-
actual: frame,
171-
})
172-
}
173-
};
174-
175-
Ok(Self {
176-
parts: frames.into_iter(),
177-
})
178-
}
179-
}
180-
181174
#[cfg(test)]
182175
mod tests {
183176
use super::*;

src/store.rs

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ impl Store {
1919
pub fn get(&self, key: &str) -> Option<&Bytes> {
2020
self.store.get(key)
2121
}
22+
23+
pub fn exists(&self, key: &str) -> bool {
24+
self.store.contains_key(key)
25+
}
2226
}
2327

2428
impl Default for Store {

0 commit comments

Comments
 (0)