beyond_resp/value.rs
1use bytes::Bytes;
2
3/// Protocol version — controls null wire encoding and enables RESP3 types.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub enum Version {
6 #[default]
7 Resp2,
8 Resp3,
9}
10
11/// A parsed RESP value covering all RESP2 and RESP3 wire types.
12///
13/// `PartialEq` is derived; `f64::NAN != f64::NAN` per IEEE 754 is correct behaviour.
14#[derive(Debug, Clone, PartialEq)]
15pub enum Value {
16 // ── RESP2 ──────────────────────────────────────────────────────────────
17
18 /// `+<str>\r\n` — short non-binary status string, e.g. `"OK"`
19 SimpleString(Bytes),
20 /// `-<msg>\r\n` — error (raw bytes, includes the kind prefix e.g. `"ERR bad"`)
21 SimpleError(Bytes),
22 /// `:<n>\r\n` — 64-bit signed integer
23 Integer(i64),
24 /// `$<len>\r\n<data>\r\n` — binary-safe bulk string
25 BulkString(Bytes),
26 /// `*<count>\r\n<elements>` — ordered array
27 Array(Vec<Value>),
28
29 // ── Unified null (RESP2: $-1\r\n / *-1\r\n ; RESP3: _\r\n) ────────────
30 Null,
31
32 // ── RESP3 ──────────────────────────────────────────────────────────────
33
34 /// `#t\r\n` / `#f\r\n`
35 Boolean(bool),
36 /// `,<value>\r\n` — IEEE 754 double; encodes `inf`, `-inf`, `nan`
37 Double(f64),
38 /// `(<decimal>\r\n` — arbitrary-precision integer as raw decimal bytes (no bignum dep)
39 BigNumber(Bytes),
40 /// `!<len>\r\n<data>\r\n` — binary-safe error payload
41 BulkError(Bytes),
42 /// `=<len>\r\n<enc>:<data>\r\n` — string with 3-byte encoding hint
43 VerbatimString { encoding: [u8; 3], data: Bytes },
44 /// `%<count>\r\n<key><value>...` — key-value map
45 Map(Vec<(Value, Value)>),
46 /// `|<count>\r\n<key><value>...<reply>` — attribute metadata + actual reply
47 Attribute { attrs: Vec<(Value, Value)>, value: Box<Value> },
48 /// `~<count>\r\n<elements>` — unordered unique set
49 Set(Vec<Value>),
50 /// `><count>\r\n<elements>` — out-of-band push message
51 Push(Vec<Value>),
52}
53
54impl Value {
55 /// Returns `true` if this value is an error type.
56 pub fn is_error(&self) -> bool {
57 matches!(self, Self::SimpleError(_) | Self::BulkError(_))
58 }
59
60 /// Returns `true` if this value is null.
61 pub fn is_null(&self) -> bool {
62 matches!(self, Self::Null)
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn is_error_covers_all_variants() {
72 assert!(Value::SimpleError(Bytes::from("ERR msg")).is_error());
73 assert!(Value::BulkError(Bytes::from("SYNTAX detail")).is_error());
74 assert!(!Value::SimpleString(Bytes::from("OK")).is_error());
75 assert!(!Value::Integer(0).is_error());
76 assert!(!Value::Null.is_error());
77 assert!(!Value::Boolean(false).is_error());
78 }
79
80 #[test]
81 fn is_null_only_for_null_variant() {
82 assert!(Value::Null.is_null());
83 assert!(!Value::Integer(0).is_null());
84 assert!(!Value::SimpleString(Bytes::from("")).is_null());
85 assert!(!Value::Boolean(false).is_null());
86 assert!(!Value::BulkString(Bytes::new()).is_null());
87 }
88}