redis_module/
redisvalue.rs

1use crate::{
2    context::call_reply::{CallResult, VerbatimStringFormat},
3    CallReply, RedisError, RedisString,
4};
5use std::{
6    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
7    hash::Hash,
8};
9
10#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
11pub enum RedisValueKey {
12    Integer(i64),
13    String(String),
14    BulkRedisString(RedisString),
15    BulkString(Vec<u8>),
16    Bool(bool),
17}
18
19#[derive(Debug, PartialEq, Clone)]
20pub enum RedisValue {
21    SimpleStringStatic(&'static str),
22    SimpleString(String),
23    BulkString(String),
24    BulkRedisString(RedisString),
25    StringBuffer(Vec<u8>),
26    Integer(i64),
27    Bool(bool),
28    Float(f64),
29    BigNumber(String),
30    VerbatimString((VerbatimStringFormat, Vec<u8>)),
31    Array(Vec<RedisValue>),
32    StaticError(&'static str),
33    Map(HashMap<RedisValueKey, RedisValue>),
34    Set(HashSet<RedisValueKey>),
35    OrderedMap(BTreeMap<RedisValueKey, RedisValue>),
36    OrderedSet(BTreeSet<RedisValueKey>),
37    Null,
38    NoReply, // No reply at all (as opposed to a Null reply)
39}
40
41impl TryFrom<RedisValue> for String {
42    type Error = RedisError;
43    fn try_from(val: RedisValue) -> Result<Self, RedisError> {
44        match val {
45            RedisValue::SimpleStringStatic(s) => Ok(s.to_string()),
46            RedisValue::SimpleString(s) => Ok(s),
47            RedisValue::BulkString(s) => Ok(s),
48            RedisValue::BulkRedisString(s) => Ok(s.try_as_str()?.to_string()),
49            RedisValue::StringBuffer(s) => Ok(std::str::from_utf8(&s)?.to_string()),
50            _ => Err(RedisError::Str("Can not convert result to String")),
51        }
52    }
53}
54
55impl From<String> for RedisValueKey {
56    fn from(s: String) -> Self {
57        Self::String(s)
58    }
59}
60
61impl From<i64> for RedisValueKey {
62    fn from(i: i64) -> Self {
63        Self::Integer(i)
64    }
65}
66
67impl From<RedisString> for RedisValueKey {
68    fn from(rs: RedisString) -> Self {
69        Self::BulkRedisString(rs)
70    }
71}
72
73impl From<Vec<u8>> for RedisValueKey {
74    fn from(s: Vec<u8>) -> Self {
75        Self::BulkString(s)
76    }
77}
78
79impl From<&str> for RedisValueKey {
80    fn from(s: &str) -> Self {
81        s.to_owned().into()
82    }
83}
84
85impl From<&String> for RedisValueKey {
86    fn from(s: &String) -> Self {
87        s.clone().into()
88    }
89}
90
91impl From<bool> for RedisValueKey {
92    fn from(b: bool) -> Self {
93        Self::Bool(b)
94    }
95}
96
97impl From<()> for RedisValue {
98    fn from(_: ()) -> Self {
99        Self::Null
100    }
101}
102
103impl From<i64> for RedisValue {
104    fn from(i: i64) -> Self {
105        Self::Integer(i)
106    }
107}
108
109impl From<bool> for RedisValue {
110    fn from(b: bool) -> Self {
111        Self::Bool(b)
112    }
113}
114
115impl From<usize> for RedisValue {
116    fn from(i: usize) -> Self {
117        (i as i64).into()
118    }
119}
120
121impl From<f64> for RedisValue {
122    fn from(f: f64) -> Self {
123        Self::Float(f)
124    }
125}
126
127impl From<String> for RedisValue {
128    fn from(s: String) -> Self {
129        Self::BulkString(s)
130    }
131}
132
133impl From<RedisString> for RedisValue {
134    fn from(s: RedisString) -> Self {
135        Self::BulkRedisString(s)
136    }
137}
138
139impl From<Vec<u8>> for RedisValue {
140    fn from(s: Vec<u8>) -> Self {
141        Self::StringBuffer(s)
142    }
143}
144
145impl From<&RedisString> for RedisValue {
146    fn from(s: &RedisString) -> Self {
147        s.clone().into()
148    }
149}
150
151impl From<&str> for RedisValue {
152    fn from(s: &str) -> Self {
153        s.to_owned().into()
154    }
155}
156
157impl From<&String> for RedisValue {
158    fn from(s: &String) -> Self {
159        s.clone().into()
160    }
161}
162
163impl<T: Into<Self>> From<Option<T>> for RedisValue {
164    fn from(s: Option<T>) -> Self {
165        s.map_or(Self::Null, Into::into)
166    }
167}
168
169impl<T: Into<Self>> From<Vec<T>> for RedisValue {
170    fn from(items: Vec<T>) -> Self {
171        Self::Array(items.into_iter().map(Into::into).collect())
172    }
173}
174
175impl<K: Into<RedisValueKey>, V: Into<RedisValue>> From<HashMap<K, V>> for RedisValue {
176    fn from(items: HashMap<K, V>) -> Self {
177        Self::Map(
178            items
179                .into_iter()
180                .map(|(k, v)| (k.into(), v.into()))
181                .collect(),
182        )
183    }
184}
185
186impl<K: Into<RedisValueKey>, V: Into<RedisValue>> From<BTreeMap<K, V>> for RedisValue {
187    fn from(items: BTreeMap<K, V>) -> Self {
188        Self::OrderedMap(
189            items
190                .into_iter()
191                .map(|(k, v)| (k.into(), v.into()))
192                .collect(),
193        )
194    }
195}
196
197impl<K: Into<RedisValueKey>> From<HashSet<K>> for RedisValue {
198    fn from(items: HashSet<K>) -> Self {
199        Self::Set(items.into_iter().map(Into::into).collect())
200    }
201}
202
203impl<K: Into<RedisValueKey>> From<BTreeSet<K>> for RedisValue {
204    fn from(items: BTreeSet<K>) -> Self {
205        Self::OrderedSet(items.into_iter().map(Into::into).collect())
206    }
207}
208
209impl<'root> TryFrom<&CallReply<'root>> for RedisValueKey {
210    type Error = RedisError;
211    fn try_from(reply: &CallReply<'root>) -> Result<Self, Self::Error> {
212        match reply {
213            CallReply::I64(reply) => Ok(RedisValueKey::Integer(reply.to_i64())),
214            CallReply::String(reply) => Ok(reply
215                .to_string()
216                .map_or(RedisValueKey::BulkString(reply.as_bytes().to_vec()), |v| {
217                    RedisValueKey::String(v)
218                })),
219            CallReply::Bool(b) => Ok(RedisValueKey::Bool(b.to_bool())),
220            _ => Err(RedisError::String(format!(
221                "Given CallReply can not be used as a map key or a set element, {:?}",
222                reply
223            ))),
224        }
225    }
226}
227
228impl<'root> From<&CallReply<'root>> for RedisValue {
229    fn from(reply: &CallReply<'root>) -> Self {
230        match reply {
231            CallReply::Unknown => RedisValue::StaticError("Error on method call"),
232            CallReply::Array(reply) => {
233                RedisValue::Array(reply.iter().map(|v| (&v).into()).collect())
234            }
235            CallReply::I64(reply) => RedisValue::Integer(reply.to_i64()),
236            CallReply::String(reply) => RedisValue::SimpleString(reply.to_string().unwrap()),
237            CallReply::Null(_) => RedisValue::Null,
238            CallReply::Map(reply) => RedisValue::Map(
239                reply
240                    .iter()
241                    .map(|(key, val)| {
242                        (
243                            (&key).try_into().unwrap_or_else(|e| {
244                                panic!("Got unhashable map key from Redis, {key:?}, {e}")
245                            }),
246                            (&val).into(),
247                        )
248                    })
249                    .collect(),
250            ),
251            CallReply::Set(reply) => RedisValue::Set(
252                reply
253                    .iter()
254                    .map(|v| {
255                        (&v).try_into().unwrap_or_else(|e| {
256                            panic!("Got unhashable set element from Redis, {v:?}, {e}")
257                        })
258                    })
259                    .collect(),
260            ),
261            CallReply::Bool(reply) => RedisValue::Bool(reply.to_bool()),
262            CallReply::Double(reply) => RedisValue::Float(reply.to_double()),
263            CallReply::BigNumber(reply) => RedisValue::BigNumber(reply.to_string().unwrap()),
264            CallReply::VerbatimString(reply) => {
265                RedisValue::VerbatimString(reply.to_parts().unwrap())
266            }
267        }
268    }
269}
270
271impl<'root> From<&CallResult<'root>> for RedisValue {
272    fn from(reply: &CallResult<'root>) -> Self {
273        reply.as_ref().map_or_else(
274            |e| {
275                // [RedisValue] does not support error, we can change that but to avoid
276                // drastic changes and try to keep backword compatability, currently
277                // we will stansform the error into a String buffer.
278                RedisValue::StringBuffer(e.as_bytes().to_vec())
279            },
280            |v| (v).into(),
281        )
282    }
283}
284
285impl<'root> TryFrom<&CallResult<'root>> for RedisValueKey {
286    type Error = RedisError;
287    fn try_from(reply: &CallResult<'root>) -> Result<Self, Self::Error> {
288        reply.as_ref().map_or_else(
289            |e| {
290                Err(RedisError::String(
291                    format!("Got an error reply which can not be translated into a map key or set element, {:?}", e),
292                ))
293            },
294            |v| v.try_into(),
295        )
296    }
297}
298
299//////////////////////////////////////////////////////////
300
301#[cfg(test)]
302mod tests {
303    use super::RedisValue;
304
305    #[test]
306    fn from_vec_string() {
307        assert_eq!(
308            RedisValue::from(vec!["foo".to_string()]),
309            RedisValue::Array(vec![RedisValue::BulkString("foo".to_owned())])
310        );
311    }
312
313    #[test]
314    fn from_vec_str() {
315        assert_eq!(
316            RedisValue::from(vec!["foo"]),
317            RedisValue::Array(vec![RedisValue::BulkString("foo".to_owned())])
318        );
319    }
320
321    #[test]
322    fn from_vec_string_ref() {
323        assert_eq!(
324            RedisValue::from(vec![&"foo".to_string()]),
325            RedisValue::Array(vec![RedisValue::BulkString("foo".to_owned())])
326        );
327    }
328
329    #[test]
330    fn from_option_str() {
331        assert_eq!(
332            RedisValue::from(Some("foo")),
333            RedisValue::BulkString("foo".to_owned())
334        );
335    }
336
337    #[test]
338    fn from_vec() {
339        let v: Vec<u8> = vec![0, 3, 5, 21, 255];
340        assert_eq!(
341            RedisValue::from(v),
342            RedisValue::StringBuffer(vec![0, 3, 5, 21, 255])
343        );
344    }
345
346    #[test]
347    fn from_option_none() {
348        assert_eq!(RedisValue::from(None::<()>), RedisValue::Null,);
349    }
350}