From ac648fe12032b90d737d1f72efe92f444ed6c030 Mon Sep 17 00:00:00 2001 From: Nicolas del Valle Date: Fri, 12 Apr 2024 11:02:25 +0200 Subject: [PATCH] Implement Ping and Select commands --- src/commands/mod.rs | 72 ++++++++++++++++++++++++++++-------------- src/commands/ping.rs | 40 +++++++++++++++++++++++ src/commands/select.rs | 34 ++++++++++++++++++++ 3 files changed, 123 insertions(+), 23 deletions(-) create mode 100644 src/commands/ping.rs create mode 100644 src/commands/select.rs diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 6bc4fc5..e2be90d 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -9,6 +9,8 @@ pub mod get; pub mod info; pub mod keys; pub mod module; +pub mod ping; +pub mod select; pub mod set; pub mod type_; @@ -23,7 +25,7 @@ use crate::store::Store; use crate::Error; use client::Client; -use command::Command as Foo; +use command::Command as Command_; use config::Config; use dbsize::DBSize; use del::Del; @@ -32,39 +34,46 @@ use get::Get; use info::Info; use keys::Keys; use module::Module; +use ping::Ping; +use select::Select; use set::Set; use type_::Type; #[derive(Debug, PartialEq)] pub enum Command { - Get(Get), - Set(Set), + DBsize(DBSize), Del(Del), + Exists(Exists), + Get(Get), Keys(Keys), - Info(Info), + Set(Set), + Type(Type), + Client(Client), - Module(Module), - Command(Foo), + Command(Command_), Config(Config), - Exists(Exists), - DBsize(DBSize), - Type(Type), + Info(Info), + Module(Module), + Ping(Ping), + Select(Select), } impl Executable for Command { fn exec(self, store: Arc>) -> Result { match self { - Command::Get(cmd) => cmd.exec(store), - Command::Set(cmd) => cmd.exec(store), - Command::Del(cmd) => cmd.exec(store), - Command::Keys(cmd) => cmd.exec(store), - Command::Info(cmd) => cmd.exec(store), Command::Client(cmd) => cmd.exec(store), - Command::Module(cmd) => cmd.exec(store), Command::Command(cmd) => cmd.exec(store), Command::Config(cmd) => cmd.exec(store), - Command::Exists(cmd) => cmd.exec(store), Command::DBsize(cmd) => cmd.exec(store), + Command::Del(cmd) => cmd.exec(store), + Command::Exists(cmd) => cmd.exec(store), + Command::Get(cmd) => cmd.exec(store), + Command::Info(cmd) => cmd.exec(store), + Command::Keys(cmd) => cmd.exec(store), + Command::Module(cmd) => cmd.exec(store), + Command::Ping(cmd) => cmd.exec(store), + Command::Select(cmd) => cmd.exec(store), + Command::Set(cmd) => cmd.exec(store), Command::Type(cmd) => cmd.exec(store), } } @@ -92,17 +101,19 @@ impl TryFrom for Command { let command_name = parser.parse_command_name()?; match &command_name[..] { - "get" => Get::try_from(parser).map(Command::Get), - "set" => Set::try_from(parser).map(Command::Set), + "client" => Client::try_from(parser).map(Command::Client), + "command" => Command_::try_from(parser).map(Command::Command), + "config" => Config::try_from(parser).map(Command::Config), + "dbsize" => DBSize::try_from(parser).map(Command::DBsize), "del" => Del::try_from(parser).map(Command::Del), - "keys" => Keys::try_from(parser).map(Command::Keys), "exists" => Exists::try_from(parser).map(Command::Exists), - "dbsize" => DBSize::try_from(parser).map(Command::DBsize), + "get" => Get::try_from(parser).map(Command::Get), "info" => Info::try_from(parser).map(Command::Info), - "client" => Client::try_from(parser).map(Command::Client), + "keys" => Keys::try_from(parser).map(Command::Keys), "module" => Module::try_from(parser).map(Command::Module), - "command" => Foo::try_from(parser).map(Command::Command), - "config" => Config::try_from(parser).map(Command::Config), + "ping" => Ping::try_from(parser).map(Command::Ping), + "select" => Select::try_from(parser).map(Command::Select), + "set" => Set::try_from(parser).map(Command::Set), "type" => Type::try_from(parser).map(Command::Type), name => Err(format!("protocol error; unknown command {:?}", name).into()), } @@ -152,6 +163,21 @@ impl CommandParser { } } + fn next_integer(&mut self) -> Result { + let frame = self + .parts + .next() + .ok_or_else(|| CommandParserError::EndOfStream)?; + + match frame { + Frame::Integer(i) => Ok(i), + frame => Err(CommandParserError::InvalidFrame { + expected: "integer".to_string(), + actual: frame, + }), + } + } + fn next_bytes(&mut self) -> Result { let frame = self .parts diff --git a/src/commands/ping.rs b/src/commands/ping.rs new file mode 100644 index 0000000..ff38f65 --- /dev/null +++ b/src/commands/ping.rs @@ -0,0 +1,40 @@ +use bytes::Bytes; +use std::sync::{Arc, Mutex}; + +use crate::commands::executable::Executable; +use crate::commands::{CommandParser, CommandParserError}; +use crate::frame::Frame; +use crate::store::Store; +use crate::Error; + +/// Returns PONG if no argument is provided, otherwise return a copy of the argument as a bulk. +/// +/// Ref: +#[derive(Debug, PartialEq)] +pub struct Ping { + pub payload: Option, +} + +impl Executable for Ping { + fn exec(self, _store: Arc>) -> Result { + let res = self + .payload + .map_or(Frame::Bulk(Bytes::from("PONG")), Frame::Bulk); + + Ok(res) + } +} + +impl TryFrom<&mut CommandParser> for Ping { + type Error = Error; + + fn try_from(parser: &mut CommandParser) -> Result { + let payload = match parser.next_bytes() { + Ok(payload) => Some(payload), + Err(CommandParserError::EndOfStream) => None, + Err(e) => return Err(e.into()), + }; + + Ok(Self { payload }) + } +} diff --git a/src/commands/select.rs b/src/commands/select.rs new file mode 100644 index 0000000..9da2a35 --- /dev/null +++ b/src/commands/select.rs @@ -0,0 +1,34 @@ +use bytes::Bytes; +use std::sync::{Arc, Mutex}; + +use crate::commands::executable::Executable; +use crate::commands::CommandParser; +use crate::frame::Frame; +use crate::store::Store; +use crate::Error; + +/// Select the Redis logical database having the specified zero-based numeric index. New +/// connections always use the database 0. +/// +/// Ref: +#[derive(Debug, PartialEq)] +pub struct Select { + /// The GUI clients we tested send this index value as bytes. Since we are not processing this + /// value, there is no need to convert it to a number for now. + pub index: Bytes, +} + +impl Executable for Select { + fn exec(self, _store: Arc>) -> Result { + Ok(Frame::Simple("OK".to_string())) + } +} + +impl TryFrom<&mut CommandParser> for Select { + type Error = Error; + + fn try_from(parser: &mut CommandParser) -> Result { + let index = parser.next_bytes()?; + Ok(Self { index }) + } +}