redis_asio/base/
value.rs

1use super::{RedisResult, RedisError, RedisErrorKind};
2use std::error::Error;
3use std::fmt;
4use std::cmp::PartialEq;
5use std::str::FromStr;
6use std::collections::HashMap;
7use std::hash::Hash;
8use core::num::ParseIntError;
9use crate::base::RespInternalValue;
10
11
12/// Set of types that are parsed to and from RESP binary packets.
13/// Represents RESP protocol: "https://redis.io/topics/protocol".
14#[derive(PartialEq, Eq, Clone, Debug)]
15pub enum RedisValue {
16    Nil,
17    Ok,
18    Status(String),
19    Int(i64),
20    BulkString(Vec<u8>),
21    Array(Vec<RedisValue>),
22}
23
24impl RedisValue {
25    pub(crate) fn from_resp_value(resp_value: RespInternalValue) -> RedisResult<RedisValue> {
26        match resp_value {
27            RespInternalValue::Nil => Ok(RedisValue::Nil),
28            RespInternalValue::Error(x) => Err(RedisError::new(RedisErrorKind::ReceiveError, x)),
29            RespInternalValue::Status(x) => match x.as_str() {
30                "OK" => Ok(RedisValue::Ok),
31                _ => Ok(RedisValue::Status(x))
32            },
33            RespInternalValue::Int(x) => Ok(RedisValue::Int(x)),
34            RespInternalValue::BulkString(x) => Ok(RedisValue::BulkString(x)),
35            RespInternalValue::Array(x) => {
36                let mut res: Vec<RedisValue> = Vec::with_capacity(x.len());
37                for val in x.into_iter() {
38                    res.push(Self::from_resp_value(val)?);
39                }
40                Ok(RedisValue::Array(res))
41            }
42        }
43    }
44}
45
46/// Trait interface requires to implement method to convert `RedisValue`
47/// into base type.
48///
49/// # Example
50///
51/// ```
52/// use redis_asio::{RedisResult, RedisError, RedisErrorKind, RedisValue,
53///                  FromRedisValue, from_redis_value};
54///
55/// #[derive(PartialEq, Debug)]
56/// struct ClientStruct{
57///     data: String,
58/// }
59///
60/// impl FromRedisValue for ClientStruct {
61///     fn from_redis_value(value: &RedisValue) -> RedisResult<Self> {
62///         match value {
63///             RedisValue::BulkString(data) => {
64///                 let data = String::from_utf8(data.clone())
65///                     .map_err(|err|
66///                         RedisError::new(RedisErrorKind::ParseError,
67///                                         "Cannot parse".to_string()))?;
68///                     Ok(ClientStruct {data})
69///             }
70///             _ => Err(RedisError::new(RedisErrorKind::ParseError,
71///                      "Cannot parse".to_string()))
72///         }
73///     }
74/// }
75///
76/// let redis_value = RedisValue::BulkString(b"some data".to_vec());
77/// let origin = ClientStruct {data: "some data".to_string()};
78/// assert_eq!(origin, from_redis_value::<ClientStruct>(&redis_value).unwrap());
79/// ```
80pub trait FromRedisValue: Sized {
81    fn from_redis_value(value: &RedisValue) -> RedisResult<Self>;
82
83    fn from_redis_u8(_: u8) -> Option<Self> {
84        None
85    }
86}
87
88/// Convert `RedisValue` into `T` value.
89///
90/// # Example
91/// ```
92/// use redis_asio::{RedisValue, from_redis_value};
93///
94/// let redis_value = RedisValue::BulkString(b"some data".to_vec());
95/// let origin = "some data".to_string();
96/// let result : String = from_redis_value::<String>(&redis_value).unwrap();
97/// assert_eq!(origin, result);
98/// ```
99pub fn from_redis_value<T: FromRedisValue>(value: &RedisValue) -> RedisResult<T> {
100    T::from_redis_value(value)
101        .map_err(|err|
102            RedisError::new(
103                err.error.clone(),
104                format!("Couldn't convert the Redis value: \"{:?}\". Reason: \"{}\"", value, err.description()
105                ),
106            )
107        )
108}
109
110impl FromRedisValue for RedisValue {
111    fn from_redis_value(value: &RedisValue) -> RedisResult<Self> {
112        Ok(value.clone())
113    }
114}
115
116impl FromRedisValue for u8 {
117    fn from_redis_value(value: &RedisValue) -> RedisResult<Self> {
118        int_from_redis_value::<u8>(value)
119    }
120
121    fn from_redis_u8(num: u8) -> Option<Self> {
122        Some(num)
123    }
124}
125
126impl FromRedisValue for String {
127    fn from_redis_value(value: &RedisValue) -> RedisResult<Self> {
128        match value {
129            RedisValue::Status(x) => Ok(x.clone()),
130            RedisValue::BulkString(x) => {
131                String::from_utf8(x.clone()).map_err(|err| to_conversion_error(err))
132            }
133            _ => Err(conversion_error_from_value(value, "String"))
134        }
135    }
136}
137
138impl<T: FromRedisValue> FromRedisValue for Vec<T> {
139    fn from_redis_value(value: &RedisValue) -> RedisResult<Self> {
140        match value {
141            RedisValue::BulkString(bulk_data) => {
142                let mut result: Vec<T> = Vec::with_capacity(bulk_data.len());
143                for num in bulk_data.iter() {
144                    match T::from_redis_u8(*num) {
145                        Some(x) => result.push(x),
146                        _ => return Err(conversion_error_from_value(bulk_data, "Vec"))
147                    }
148                }
149                Ok(result)
150            }
151            RedisValue::Array(x) => {
152                let mut result: Vec<T> = Vec::with_capacity(x.len());
153                for val in x.iter() {
154                    match from_redis_value(val) {
155                        Ok(x) => result.push(x),
156                        Err(err) => return Err(err),
157                    }
158                }
159                Ok(result)
160            }
161            _ => Err(conversion_error_from_value(value, "Array"))
162        }
163    }
164}
165
166impl<K: FromRedisValue + Eq + Hash, V: FromRedisValue> FromRedisValue for HashMap<K, V> {
167    fn from_redis_value(value: &RedisValue) -> RedisResult<Self> {
168        match value {
169            RedisValue::Array(key_values) => {
170                const KEY_VALUE_CHUNK_LEN: usize = 2;
171                const KEY_POS: usize = 0;
172                const VALUE_POS: usize = 1;
173
174                // count of keys and values should be evenl
175                if key_values.len() % KEY_VALUE_CHUNK_LEN != 0 {
176                    return Err(conversion_error_from_value(value, "HashMap"));
177                }
178
179                let mut result =
180                    HashMap::with_capacity(key_values.len() / KEY_VALUE_CHUNK_LEN);
181
182                for chunk in key_values.chunks_exact(KEY_VALUE_CHUNK_LEN) {
183                    let key: K = from_redis_value(&chunk[KEY_POS])?;
184                    let value: V = from_redis_value(&chunk[VALUE_POS])?;
185                    result.insert(key, value);
186                }
187
188                Ok(result)
189            }
190            _ => Err(conversion_error_from_value(value, "HashMap"))
191        }
192    }
193}
194
195// TODO make macro and implement that for (T, ..., T)
196impl<T1, T2> FromRedisValue for (T1, T2)
197    where T1: FromRedisValue + fmt::Debug,
198          T2: FromRedisValue + fmt::Debug {
199    fn from_redis_value(value: &RedisValue) -> RedisResult<Self> {
200        let values: Vec<RedisValue> = from_redis_value(value)?;
201        if values.len() != 2 {
202            return Err(
203                RedisError::new(
204                    RedisErrorKind::ParseError,
205                    format!("Couldn't convert the Redis value: \"{:?}\" to tuple of 2 elements",
206                            values)));
207        }
208
209        let first: T1 = from_redis_value(&values[0])?;
210        let second: T2 = from_redis_value(&values[1])?;
211
212        Ok((first, second))
213    }
214}
215
216fn to_conversion_error<T>(err: T) -> RedisError
217    where T: Error {
218    RedisError::new(RedisErrorKind::IncorrectConversion, err.description().to_string())
219}
220
221fn conversion_error_from_value<T>(src_value: &T, dst_type: &str) -> RedisError
222    where T: fmt::Debug {
223    RedisError::new(RedisErrorKind::IncorrectConversion,
224                    format!("{:?} is not convertible to {}", src_value, dst_type))
225}
226
227fn int_from_redis_value<T>(value: &RedisValue) -> RedisResult<T>
228    where T: ToIntConvertible {
229    match value {
230        RedisValue::Int(x) => Ok(T::convert_from_int(*x)),
231        RedisValue::BulkString(x) => {
232            match String::from_utf8(x.clone()) {
233                Ok(xstr) =>
234                    T::convert_from_str(xstr)
235                        .map_err(|_| conversion_error_from_value(&x, "i64")),
236                Err(_) => Err(conversion_error_from_value(x, "i64"))
237            }
238        }
239        _ => Err(conversion_error_from_value(value, "i64"))
240    }
241}
242
243trait ToIntConvertible: Sized + FromStr {
244    fn convert_from_str(val: String) -> Result<Self, ParseIntError>;
245    fn convert_from_int(val: i64) -> Self;
246}
247
248impl ToIntConvertible for u8 {
249    fn convert_from_str(val: String) -> Result<u8, ParseIntError> { val.parse::<u8>() }
250    fn convert_from_int(val: i64) -> u8 { val as u8 }
251}
252
253macro_rules! declare_to_int_convertible {
254    ($itype:ty) => {
255        impl ToIntConvertible for $itype {
256            fn convert_from_str(val: String) -> Result<$itype, ParseIntError> { val.parse::<$itype>() }
257            fn convert_from_int(val: i64) -> $itype { val as $itype }
258        }
259
260        impl FromRedisValue for $itype {
261            fn from_redis_value(value: &RedisValue) -> RedisResult<Self> {
262                int_from_redis_value::<$itype>(value)
263            }
264        }
265    };
266}
267
268declare_to_int_convertible!(i8);
269declare_to_int_convertible!(i16);
270declare_to_int_convertible!(u16);
271declare_to_int_convertible!(i32);
272declare_to_int_convertible!(u32);
273declare_to_int_convertible!(i64);
274declare_to_int_convertible!(u64);
275declare_to_int_convertible!(isize);
276declare_to_int_convertible!(usize);
277
278#[cfg(test)]
279mod tests {
280    use super::*;
281
282    #[test]
283    fn common_test_from_redis_value() {
284        #[derive(PartialEq, Debug)]
285        struct ArrayNode { data: String }
286
287        impl FromRedisValue for ArrayNode {
288            fn from_redis_value(value: &RedisValue) -> RedisResult<Self> {
289                Ok(ArrayNode { data: from_redis_value(value)? })
290            }
291        }
292
293        let value = RedisValue::Array(
294            vec![RedisValue::BulkString(String::from("data1").into_bytes()),
295                 RedisValue::BulkString(String::from("data2").into_bytes())]);
296
297        let origin = vec![ArrayNode { data: String::from("data1") },
298                          ArrayNode { data: String::from("data2") }];
299        assert_eq!(origin, from_redis_value::<Vec<ArrayNode>>(&value).unwrap());
300    }
301
302    #[test]
303    fn test_from_nil_value() {
304        let val = RedisValue::Nil;
305        assert!(from_redis_value::<i64>(&val).is_err(), "expected Err");
306        assert!(from_redis_value::<String>(&val).is_err(), "expected Err");
307        assert!(from_redis_value::<Vec<i64>>(&val).is_err(), "expected Err");
308    }
309
310    #[test]
311    fn test_from_ok_value() {
312        let val = RedisValue::Ok;
313        assert!(from_redis_value::<i64>(&val).is_err(), "expected Err");
314        assert!(from_redis_value::<String>(&val).is_err(), "expected Err");
315        assert!(from_redis_value::<Vec<i64>>(&val).is_err(), "expected Err");
316    }
317
318    #[test]
319    fn test_from_status_value() {
320        let val = RedisValue::Status(String::from("Status"));
321        assert_eq!(String::from("Status"), from_redis_value::<String>(&val).unwrap());
322        assert!(from_redis_value::<i64>(&val).is_err(), "expected Err");
323        assert!(from_redis_value::<Vec<i64>>(&val).is_err(), "expected Err");
324    }
325
326    #[test]
327    fn test_from_int_value() {
328        let src: i64 = std::i64::MAX - 5;
329        let val = RedisValue::Int(src);
330        assert_eq!(src as i8, from_redis_value::<i8>(&val).unwrap());
331        assert_eq!(src as u8, from_redis_value::<u8>(&val).unwrap());
332        assert_eq!(src as i16, from_redis_value::<i16>(&val).unwrap());
333        assert_eq!(src as u16, from_redis_value::<u16>(&val).unwrap());
334        assert_eq!(src as i32, from_redis_value::<i32>(&val).unwrap());
335        assert_eq!(src as u32, from_redis_value::<u32>(&val).unwrap());
336        assert_eq!(src as i64, from_redis_value::<i64>(&val).unwrap());
337        assert_eq!(src as u64, from_redis_value::<u64>(&val).unwrap());
338        assert!(from_redis_value::<String>(&val).is_err(), "expected Err");
339        assert!(from_redis_value::<Vec<i64>>(&val).is_err(), "expected Err");
340    }
341
342    #[test]
343    fn test_from_bulkstring_value() {
344        let raw_data = vec![1, 2, 250, 251, 255];
345        let string_data = String::from("BulkString");
346        let val1 = RedisValue::BulkString(raw_data.clone());
347        let val2 = RedisValue::BulkString(string_data.clone().into_bytes());
348
349        assert!(from_redis_value::<String>(&val1).is_err(),
350                "expected Err on cannot convert raw data to String");
351        assert_eq!(raw_data, from_redis_value::<Vec<u8>>(&val1).unwrap());
352        assert!(from_redis_value::<Vec<i8>>(&val1).is_err(), "expected Err");
353        assert!(from_redis_value::<Vec<i64>>(&val1).is_err(), "expected Err");
354        assert!(from_redis_value::<i64>(&val1).is_err(), "expected Err");
355
356        assert_eq!(string_data, from_redis_value::<String>(&val2).unwrap());
357        assert_eq!(string_data.into_bytes(), from_redis_value::<Vec<u8>>(&val2).unwrap());
358    }
359
360    #[test]
361    fn test_from_array_value() {
362        let data
363            = vec![RedisValue::Nil,
364                   RedisValue::Ok,
365                   RedisValue::Status(String::from("Status")),
366                   RedisValue::Int(12345),
367                   RedisValue::BulkString(vec![1, 2, 3, 4, 5]),
368                   RedisValue::Array(
369                       vec![RedisValue::Int(9876),
370                            RedisValue::BulkString(String::from("BulkString").into_bytes())])];
371
372        let val1 = RedisValue::Array(data.clone());
373        assert_eq!(data, from_redis_value::<Vec<RedisValue>>(&val1).unwrap());
374    }
375}