Skip to content

Commit

Permalink
hash: Add delete() command
Browse files Browse the repository at this point in the history
  • Loading branch information
XuShaohua committed Jun 12, 2024
1 parent 3e13f31 commit 8bf693a
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 22 deletions.
16 changes: 9 additions & 7 deletions src/cmd/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::cmd::parse::{ParseCommandError, Parser};

#[derive(Debug, Clone)]
pub enum GenericCommand {
Delete(Vec<String>),
Exists(Vec<String>),
Delete(String, Option<Vec<String>>),
Exists(String, Option<Vec<String>>),
RandomKey(usize),
Rename(String, String),
Type(String),
Expand All @@ -23,12 +23,14 @@ impl GenericCommand {
) -> Result<Option<Command>, ParseCommandError> {
let generic_cmd = match cmd_name {
"del" => {
let keys = parser.remaining_strings()?;
Self::Delete(keys)
let key = parser.next_string()?;
let extra_keys = parser.remaining_strings()?;
Self::Delete(key, extra_keys)
}
"exists" => {
let keys = parser.remaining_strings()?;
Self::Exists(keys)
let key = parser.next_string()?;
let extra_keys = parser.remaining_strings()?;
Self::Exists(key, extra_keys)
}
"randomkey" => {
let mut rng = rand::thread_rng();
Expand All @@ -48,4 +50,4 @@ impl GenericCommand {
};
Ok(Some(Command::Generic(generic_cmd)))
}
}
}
7 changes: 7 additions & 0 deletions src/cmd/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub type ExtraValues = Option<Vec<(String, Vec<u8>)>>;

#[derive(Debug, Clone)]
pub enum HashCommand {
Del(String, String, Option<Vec<String>>),
Get(String, String),
GetAll(String),
Keys(String),
Expand All @@ -24,6 +25,12 @@ impl HashCommand {
parser: &mut Parser,
) -> Result<Option<Command>, ParseCommandError> {
let list_cmd = match cmd_name {
"hdel" => {
let key = parser.next_string()?;
let field = parser.next_string()?;
let extra_fields = parser.remaining_strings()?;
Self::Del(key, field, extra_fields)
}
"hget" => {
let key = parser.next_string()?;
let field = parser.next_string()?;
Expand Down
8 changes: 6 additions & 2 deletions src/cmd/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl Parser {
}
}

pub fn remaining_strings(&mut self) -> Result<Vec<String>, ParseCommandError> {
pub fn remaining_strings(&mut self) -> Result<Option<Vec<String>>, ParseCommandError> {
let mut list = Vec::new();
while let Some(frame) = self.iter.next() {
match frame {
Expand All @@ -113,7 +113,11 @@ impl Parser {
}
}
}
Ok(list)
if list.is_empty() {
Ok(None)
} else {
Ok(Some(list))
}
}

pub fn remaining_pairs(&mut self) -> Result<Option<Vec<(String, Vec<u8>)>>, ParseCommandError> {
Expand Down
15 changes: 10 additions & 5 deletions src/mem/generic/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ use crate::cmd::reply_frame::ReplyFrame;
use crate::mem::db::Db;

/// Removes the specified keys. A key is ignored if it does not exist.
pub fn delete(db: &mut Db, keys: Vec<String>) -> ReplyFrame {
pub fn delete(db: &mut Db, key: &str, extra_keys: Option<Vec<String>>) -> ReplyFrame {
let mut count: usize = 0;
for key in keys {
if let Some(_value) = db.remove(&key) {
count += 1;
if let Some(_value) = db.remove(key) {
count += 1;
}
if let Some(extra_keys) = extra_keys {
for key in extra_keys {
if let Some(_value) = db.remove(&key) {
count += 1;
}
}
}

ReplyFrame::Usize(count)
}
}
15 changes: 12 additions & 3 deletions src/mem/generic/exists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,16 @@ use crate::mem::db::Db;
/// The user should be aware that if the same existing key is mentioned in the arguments multiple times,
/// it will be counted multiple times.
/// So if `somekey` exists, `EXISTS somekey somekey` will return 2.
pub fn exists(db: &Db, keys: Vec<String>) -> ReplyFrame {
let count = keys.into_iter().filter(|key| db.contains_key(key.as_str())).count();
pub fn exists(db: &Db, key: &str, extra_keys: Option<Vec<String>>) -> ReplyFrame {
let mut count = 0;
if db.contains_key(key) {
count += 1;
}
if let Some(extra_keys) = extra_keys {
count += extra_keys
.iter()
.filter(|key| db.contains_key(key.as_str()))
.count();
}
ReplyFrame::Usize(count)
}
}
16 changes: 11 additions & 5 deletions src/mem/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,23 @@ use crate::cmd::reply_frame::ReplyFrame;
use crate::mem::Mem;

mod delete;
mod get_type;
mod rename;
mod exists;
mod get_type;
mod random_key;
mod rename;

impl Mem {
pub fn handle_generic_command(&mut self, command: GenericCommand) -> ReplyFrame {
match command {
GenericCommand::Delete(keys) => delete::delete(&mut self.db, keys),
GenericCommand::Exists(keys) => exists::exists(&mut self.db, keys),
GenericCommand::RandomKey(random_index) => random_key::random_key(&self.db, random_index),
GenericCommand::Delete(key, extra_keys) => {
delete::delete(&mut self.db, &key, extra_keys)
}
GenericCommand::Exists(key, extra_keys) => {
exists::exists(&mut self.db, &key, extra_keys)
}
GenericCommand::RandomKey(random_index) => {
random_key::random_key(&self.db, random_index)
}
GenericCommand::Rename(key, new_key) => rename::rename(&mut self.db, key, new_key),
GenericCommand::Type(key) => get_type::get_type(&self.db, &key),
}
Expand Down
67 changes: 67 additions & 0 deletions src/mem/hash/delete.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) 2024 Xu Shaohua <[email protected]>. All rights reserved.
// Use of this source is governed by GNU Affero General Public License
// that can be found in the LICENSE file.

use crate::cmd::reply_frame::ReplyFrame;
use crate::mem::db::{Db, MemObject};

/// Removes the specified fields from the hash stored at key.
///
/// Specified fields that do not exist within this hash are ignored.
/// If key does not exist, it is treated as an empty hash and this command returns 0.
///
/// Reply:
/// - Integer reply: The number of fields that were removed from the hash,
/// excluding any specified but non-existing fields.
pub fn delete(
db: &mut Db,
key: &str,
field: &str,
extra_fields: Option<Vec<String>>,
) -> ReplyFrame {
match db.get_mut(key) {
Some(MemObject::Hash(old_hash)) => {
let mut count = 0;
if old_hash.remove(field).is_some() {
count += 1;
}
if let Some(extra_fields) = extra_fields {
for field in &extra_fields {
if old_hash.remove(field).is_some() {
count += 1;
}
}
}

ReplyFrame::Usize(count)
}
Some(_) => ReplyFrame::wrong_type_err(),
None => ReplyFrame::zero(),
}
}

#[cfg(test)]
mod tests {
use crate::cmd::reply_frame::ReplyFrame;
use crate::mem::db::Db;
use crate::mem::hash::delete::delete;
use crate::mem::hash::set::set;

#[test]
fn test_delete() {
let mut db = Db::new();
let key = "myhash".to_owned();
let reply = set(
&mut db,
key.clone(),
"field1".to_owned(),
b"foo".to_vec(),
None,
);
assert_eq!(reply, ReplyFrame::one());
let reply = delete(&mut db, &key, "field1", None);
assert_eq!(reply, ReplyFrame::one());
let reply = delete(&mut db, &key, "field2", None);
assert_eq!(reply, ReplyFrame::zero());
}
}
4 changes: 4 additions & 0 deletions src/mem/hash/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::cmd::hash::{ExtraValues, HashCommand};
use crate::cmd::reply_frame::ReplyFrame;
use crate::mem::Mem;

mod delete;
mod get;
mod get_all;
mod keys;
Expand All @@ -21,6 +22,9 @@ pub type HashObject = HashMap<String, Vec<u8>>;
impl Mem {
pub fn handle_hash_command(&mut self, command: HashCommand) -> ReplyFrame {
match command {
HashCommand::Del(key, field, extra_fields) => {
delete::delete(&mut self.db, &key, &field, extra_fields)
}
HashCommand::Get(key, field) => get::get(&self.db, &key, &field),
HashCommand::GetAll(key) => get_all::get_all(&self.db, &key),
HashCommand::Keys(key) => keys::keys(&self.db, &key),
Expand Down

0 comments on commit 8bf693a

Please sign in to comment.