respite/
value.rs

1use crate::RespPrimitive;
2use bytes::Bytes;
3use ordered_float::OrderedFloat;
4use std::collections::{BTreeMap, BTreeSet};
5
6/// A RESP value, possibly built from many frames.
7///
8/// These values are meant to be used for testing, and thus can be hashed and compared. However,
9/// this also makes them far less performant than reading frames directly.
10#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
11pub enum RespValue {
12    Attribute(BTreeMap<RespPrimitive, RespValue>),
13    Array(Vec<RespValue>),
14    Bignum(Bytes),
15    Boolean(bool),
16    Double(OrderedFloat<f64>),
17    Error(Bytes),
18    Integer(i64),
19    Map(BTreeMap<RespPrimitive, RespValue>),
20    Nil,
21    Push(Vec<RespValue>),
22    Set(BTreeSet<RespPrimitive>),
23    String(Bytes),
24    Verbatim(Bytes, Bytes),
25}
26
27impl From<bool> for RespValue {
28    fn from(value: bool) -> Self {
29        RespValue::Boolean(value)
30    }
31}
32
33impl From<i32> for RespValue {
34    fn from(value: i32) -> Self {
35        RespValue::Integer(value.into())
36    }
37}
38
39impl From<i64> for RespValue {
40    fn from(value: i64) -> Self {
41        RespValue::Integer(value)
42    }
43}
44
45impl From<f64> for RespValue {
46    fn from(value: f64) -> Self {
47        RespValue::Double(value.into())
48    }
49}
50
51impl From<String> for RespValue {
52    fn from(value: String) -> Self {
53        RespValue::String(value.into())
54    }
55}
56
57impl From<&'static str> for RespValue {
58    fn from(value: &'static str) -> Self {
59        RespValue::String(value.into())
60    }
61}
62
63impl<const N: usize> From<&'static [u8; N]> for RespValue {
64    fn from(value: &'static [u8; N]) -> Self {
65        RespValue::String((&value[..]).into())
66    }
67}
68
69impl From<Vec<u8>> for RespValue {
70    fn from(value: Vec<u8>) -> Self {
71        RespValue::String(value.into())
72    }
73}
74
75impl RespValue {
76    /// Extract a [`Vec`] of values, if this value is an array.
77    pub fn array(&mut self) -> Option<&mut Vec<RespValue>> {
78        if let RespValue::Array(value) = self {
79            Some(value)
80        } else {
81            None
82        }
83    }
84
85    /// Extract an error message if this value is an error.
86    pub fn error(&self) -> Option<&str> {
87        if let RespValue::Error(value) = self {
88            std::str::from_utf8(value).ok()
89        } else {
90            None
91        }
92    }
93
94    /// Extract an [`i64`] if this value is an integer.
95    pub fn integer(&self) -> Option<i64> {
96        if let RespValue::Integer(i) = self {
97            Some(*i)
98        } else {
99            None
100        }
101    }
102
103    /// Extract the text value of this value if it has one.
104    pub fn text(&self) -> Option<&str> {
105        use RespValue::*;
106
107        if let String(text) | Verbatim(_, text) = self {
108            std::str::from_utf8(text).ok()
109        } else {
110            None
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn array() {
121        assert_eq!(RespValue::Array(vec![]), resp! { [] });
122        assert_eq!(
123            RespValue::Array(vec![RespValue::Array(vec![])]),
124            resp! { [[]] }
125        );
126        assert_eq!(RespValue::Array(vec![RespValue::Nil]), resp! { [nil] });
127        assert_eq!(RespValue::Array(vec![1i64.into()]), resp! { [1i64] });
128        assert_eq!(
129            RespValue::Array(vec![1i64.into(), 2i64.into()]),
130            resp! { [1i64, 2i64] }
131        );
132    }
133
134    #[test]
135    fn bignum() {
136        assert_eq!(RespValue::Bignum("1234".into()), resp! { (big "1234") });
137        assert_eq!(
138            RespValue::Array(vec![RespValue::Bignum("1234".into())]),
139            resp! { [(big "1234")] }
140        );
141    }
142
143    #[test]
144    fn boolean() {
145        assert_eq!(RespValue::Boolean(true), resp! { true });
146        assert_eq!(RespValue::Boolean(false), resp! { false });
147        assert_eq!(
148            RespValue::Array(vec![true.into(), false.into()]),
149            resp! { [true, false] }
150        );
151    }
152
153    #[test]
154    fn string() {
155        assert_eq!(RespValue::String("1234".into()), resp! { "1234" });
156    }
157
158    #[test]
159    fn double() {
160        assert_eq!(RespValue::Double(1f64.into()), resp! { 1f64 });
161    }
162
163    #[test]
164    fn integer() {
165        assert_eq!(RespValue::Integer(1i64), resp! { 1i64 });
166        assert_eq!(RespValue::Integer(-1i64), resp! { (-1) });
167    }
168
169    #[test]
170    fn map() {
171        // Bytes is a false positive here.
172        // <https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type>
173        #[allow(clippy::mutable_key_type)]
174        let mut map = BTreeMap::new();
175        map.insert("x".into(), "1".into());
176        map.insert(1i64.into(), 1f64.into());
177        map.insert(RespPrimitive::Nil, RespValue::Nil);
178        assert_eq!(
179            RespValue::Map(map),
180            resp! { {"x" => "1", 1i64 => 1f64, nil => nil} }
181        );
182    }
183
184    #[test]
185    fn nil() {
186        assert_eq!(RespValue::Nil, resp! { nil });
187    }
188
189    #[test]
190    fn push() {
191        assert_eq!(RespValue::Push(vec![]), resp! { [>] });
192        assert_eq!(RespValue::Push(vec![RespValue::Nil]), resp! { [> nil] });
193        assert_eq!(RespValue::Push(vec![1i64.into()]), resp! { [> 1i64] });
194    }
195
196    #[test]
197    fn set() {
198        // Bytes is a false positive here.
199        // <https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type>
200        #[allow(clippy::mutable_key_type)]
201        let mut set = BTreeSet::new();
202        set.insert("x".into());
203        set.insert("y".into());
204        set.insert(RespPrimitive::Nil);
205        assert_eq!(RespValue::Set(set), resp! { {"x", "y", nil} });
206    }
207
208    #[test]
209    fn verbatim() {
210        assert_eq!(
211            RespValue::Verbatim("txt".into(), "abc".into()),
212            resp! { (= "txt", "abc") }
213        );
214    }
215
216    #[test]
217    fn error() {
218        assert_eq!(
219            RespValue::Error("ERR stuff".into()),
220            resp! { (! "ERR stuff") }
221        );
222    }
223
224    #[test]
225    fn text_values() {
226        let value = RespValue::Verbatim("txt".into(), "abc".into());
227        assert_eq!(value.text(), Some("abc"));
228
229        let value = RespValue::String("abc".into());
230        assert_eq!(value.text(), Some("abc"));
231
232        let value = RespValue::Nil;
233        assert_eq!(value.text(), None);
234
235        let value = RespValue::Integer(23);
236        assert_eq!(value.text(), None);
237    }
238
239    #[test]
240    fn error_values() {
241        let value = RespValue::Verbatim("txt".into(), "abc".into());
242        assert_eq!(value.error(), None);
243
244        let value = RespValue::String("abc".into());
245        assert_eq!(value.error(), None);
246
247        let value = RespValue::Nil;
248        assert_eq!(value.error(), None);
249
250        let value = RespValue::Integer(23);
251        assert_eq!(value.error(), None);
252
253        let value = RespValue::Error("error".into());
254        assert_eq!(value.error(), Some("error"));
255    }
256
257    #[test]
258    fn integer_values() {
259        let value = RespValue::Verbatim("txt".into(), "abc".into());
260        assert_eq!(value.integer(), None);
261
262        let value = RespValue::String("abc".into());
263        assert_eq!(value.integer(), None);
264
265        let value = RespValue::Nil;
266        assert_eq!(value.integer(), None);
267
268        let value = RespValue::Integer(23);
269        assert_eq!(value.integer(), Some(23));
270
271        let value = RespValue::Error("error".into());
272        assert_eq!(value.integer(), None);
273    }
274
275    #[test]
276    fn array_values() {
277        let mut value = RespValue::Verbatim("txt".into(), "abc".into());
278        assert_eq!(value.array(), None);
279
280        let mut value = RespValue::String("abc".into());
281        assert_eq!(value.array(), None);
282
283        let mut value = RespValue::Nil;
284        assert_eq!(value.array(), None);
285
286        let mut value = RespValue::Integer(23);
287        assert_eq!(value.array(), None);
288
289        let mut value = RespValue::Error("error".into());
290        assert_eq!(value.array(), None);
291
292        let mut value = RespValue::Array(vec![RespValue::Integer(1), RespValue::Integer(2)]);
293        assert_eq!(
294            value.array(),
295            Some(&mut vec![RespValue::Integer(1), RespValue::Integer(2)])
296        );
297    }
298}