From 82c47537a396f823e412db109f1ead4d6ba1fb36 Mon Sep 17 00:00:00 2001 From: "Meir Shpilraien (Spielrein)" Date: Thu, 11 May 2023 15:33:04 +0300 Subject: [PATCH] Use `BTreeMap` and `BTreeSet` in addition to `HashMap` and `HashSet`. (#327) The main problem with HashMap and HashSet is that those are unordered. So we get results in different order each time. Using BTreeMap and BTreeSet we will get the results in the same order each time. --- examples/response.rs | 10 +++++----- src/context/mod.rs | 20 ++++++++++++++++++++ src/redisvalue.rs | 6 ++++-- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/examples/response.rs b/examples/response.rs index 543c46d1..2d76180e 100644 --- a/examples/response.rs +++ b/examples/response.rs @@ -2,7 +2,7 @@ use redis_module::{ redis_module, redisvalue::RedisValueKey, Context, NextArg, RedisError, RedisResult, RedisString, RedisValue, }; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet}; fn map_mget(ctx: &Context, args: Vec) -> RedisResult { if args.len() < 2 { @@ -19,14 +19,14 @@ fn map_mget(ctx: &Context, args: Vec) -> RedisResult { let res = match values { None => RedisValue::Null, Some(values) => { - let mut map: HashMap = HashMap::with_capacity(fields.len()); + let mut map: BTreeMap = BTreeMap::new(); for (field, value) in values.into_iter() { map.insert( RedisValueKey::BulkRedisString(field), RedisValue::BulkRedisString(value), ); } - RedisValue::Map(map) + RedisValue::OrderedMap(map) } }; @@ -48,11 +48,11 @@ fn map_unique(ctx: &Context, args: Vec) -> RedisResult { let res = match values { None => RedisValue::Null, Some(values) => { - let mut set: HashSet = HashSet::new(); + let mut set: BTreeSet = BTreeSet::new(); for (_, value) in values.into_iter() { set.insert(RedisValueKey::BulkRedisString(value)); } - RedisValue::Set(set) + RedisValue::OrderedSet(set) } }; diff --git a/src/context/mod.rs b/src/context/mod.rs index 1dd06333..ddebd280 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -466,6 +466,17 @@ impl Context { raw::Status::Ok } + Ok(RedisValue::OrderedMap(map)) => { + raw::reply_with_map(self.ctx, map.len() as c_long); + + for (key, value) in map { + self.reply_with_key(key); + self.reply(Ok(value)); + } + + raw::Status::Ok + } + Ok(RedisValue::Set(set)) => { raw::reply_with_set(self.ctx, set.len() as c_long); set.into_iter().for_each(|e| { @@ -475,6 +486,15 @@ impl Context { raw::Status::Ok } + Ok(RedisValue::OrderedSet(set)) => { + raw::reply_with_set(self.ctx, set.len() as c_long); + set.into_iter().for_each(|e| { + self.reply_with_key(e); + }); + + raw::Status::Ok + } + Ok(RedisValue::Null) => raw::reply_with_null(self.ctx), Ok(RedisValue::NoReply) => raw::Status::Ok, diff --git a/src/redisvalue.rs b/src/redisvalue.rs index 53972a7a..f8ef009b 100644 --- a/src/redisvalue.rs +++ b/src/redisvalue.rs @@ -3,11 +3,11 @@ use crate::{ CallReply, RedisError, RedisString, }; use std::{ - collections::{HashMap, HashSet}, + collections::{BTreeMap, BTreeSet, HashMap, HashSet}, hash::Hash, }; -#[derive(Debug, PartialEq, Eq, Hash, Clone)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)] pub enum RedisValueKey { Integer(i64), String(String), @@ -32,6 +32,8 @@ pub enum RedisValue { StaticError(&'static str), Map(HashMap), Set(HashSet), + OrderedMap(BTreeMap), + OrderedSet(BTreeSet), Null, NoReply, // No reply at all (as opposed to a Null reply) }