Skip to main content

netget/server/redis/
resp_value.rs

1//! Replies the server composes itself — pub/sub confirmations and messages, `PUBSUB`, and the
2//! `CLUSTER` introspection family — as a small typed tree, encoded for the connection's RESP
3//! version.
4//!
5//! The model-facing verbs encode JSON (`resp3.rs`), where a nested array in RESP2 has to fall
6//! back to JSON text. These shapes are fixed by Redis — `CLUSTER SLOTS` is an array of arrays,
7//! `CLUSTER SHARDS` an array of maps — so they need real nesting on both versions. Every tree
8//! built here is a literal shape a few levels deep; nothing a peer or the model sends decides
9//! its depth.
10
11/// One RESP value.
12#[derive(Debug, Clone, PartialEq)]
13pub enum Resp {
14    /// A bulk string (`$`).
15    Bulk(Vec<u8>),
16    /// A simple string (`+`).
17    Simple(String),
18    /// An integer (`:`).
19    Int(i64),
20    /// A null: `$-1` on RESP2, `_` on RESP3.
21    Null,
22    /// An array (`*`).
23    Array(Vec<Resp>),
24    /// A map (`%`); a flat key/value array on RESP2.
25    Map(Vec<(Resp, Resp)>),
26    /// An out-of-band push (`>`); on RESP2, where there is no push frame, an array — which is
27    /// exactly how Redis frames pub/sub messages there.
28    Push(Vec<Resp>),
29    /// Text a client shows as is: a RESP3 verbatim string (`=`, format `txt`), a bulk string on
30    /// RESP2. Redis answers `CLUSTER NODES` and `CLUSTER INFO` this way.
31    Verbatim(String),
32}
33
34impl Resp {
35    pub fn bulk(s: impl AsRef<[u8]>) -> Resp {
36        Resp::Bulk(s.as_ref().to_vec())
37    }
38
39    /// Encode for a connection speaking RESP `proto` (2 or 3).
40    pub fn encode(&self, proto: u8) -> Vec<u8> {
41        let mut out = Vec::new();
42        self.encode_into(proto, &mut out);
43        out
44    }
45
46    fn encode_into(&self, proto: u8, out: &mut Vec<u8>) {
47        use super::actions::{encode_bulk_string, encode_integer, encode_simple_string};
48        match self {
49            Resp::Bulk(b) => out.extend_from_slice(&encode_bulk_string(b)),
50            Resp::Simple(s) => out.extend_from_slice(&encode_simple_string(s)),
51            Resp::Int(i) => out.extend_from_slice(&encode_integer(*i)),
52            Resp::Null => out.extend_from_slice(&super::resp3::null(proto)),
53            Resp::Array(items) => {
54                out.extend_from_slice(format!("*{}\r\n", items.len()).as_bytes());
55                for item in items {
56                    item.encode_into(proto, out);
57                }
58            }
59            Resp::Push(items) => {
60                let marker = if proto == 3 { '>' } else { '*' };
61                out.extend_from_slice(format!("{marker}{}\r\n", items.len()).as_bytes());
62                for item in items {
63                    item.encode_into(proto, out);
64                }
65            }
66            Resp::Map(entries) => {
67                if proto == 3 {
68                    out.extend_from_slice(format!("%{}\r\n", entries.len()).as_bytes());
69                } else {
70                    out.extend_from_slice(format!("*{}\r\n", entries.len() * 2).as_bytes());
71                }
72                for (k, v) in entries {
73                    k.encode_into(proto, out);
74                    v.encode_into(proto, out);
75                }
76            }
77            Resp::Verbatim(text) => {
78                if proto == 3 {
79                    let body = format!("txt:{text}");
80                    out.extend_from_slice(format!("={}\r\n", body.len()).as_bytes());
81                    out.extend_from_slice(body.as_bytes());
82                    out.extend_from_slice(b"\r\n");
83                } else {
84                    out.extend_from_slice(&encode_bulk_string(text.as_bytes()));
85                }
86            }
87        }
88    }
89}