1use anyhow::{bail, ensure, Context, Result};
13use serde_json::Value;
14
15pub const MAX_REPLY_DEPTH: usize = 32;
17
18#[derive(Debug, Default, PartialEq)]
20pub struct Hello {
21 pub protover: Option<u8>,
22 pub auth: Option<(String, String)>,
23 pub setname: Option<String>,
24}
25
26pub fn parse_hello(args: &[String]) -> std::result::Result<Hello, String> {
29 let mut hello = Hello::default();
30 let mut rest = args;
31 if let Some(first) = rest.first() {
32 let v: i64 = first
33 .parse()
34 .map_err(|_| "ERR Protocol version is not an integer or out of range".to_string())?;
35 if v != 2 && v != 3 {
36 return Err("NOPROTO unsupported protocol version".into());
37 }
38 hello.protover = Some(v as u8);
39 rest = &rest[1..];
40 }
41 while let Some(opt) = rest.first() {
42 if opt.eq_ignore_ascii_case("AUTH") && rest.len() >= 3 {
43 hello.auth = Some((rest[1].clone(), rest[2].clone()));
44 rest = &rest[3..];
45 } else if opt.eq_ignore_ascii_case("SETNAME") && rest.len() >= 2 {
46 hello.setname = Some(rest[1].clone());
47 rest = &rest[2..];
48 } else {
49 return Err(format!("ERR Syntax error in HELLO option '{opt}'"));
50 }
51 }
52 Ok(hello)
53}
54
55pub fn hello_reply(proto: u8, connection_id: u64, mode: &str) -> Vec<u8> {
58 let fields: Vec<(&str, Value)> = vec![
59 ("server", Value::from("redis")),
60 ("version", Value::from(REPORTED_VERSION)),
61 ("proto", Value::from(proto)),
62 ("id", Value::from(connection_id)),
63 ("mode", Value::from(mode)),
64 ("role", Value::from("master")),
65 ("modules", Value::Array(vec![])),
66 ];
67 let mut out = Vec::new();
68 if proto == 3 {
69 out.extend_from_slice(format!("%{}\r\n", fields.len()).as_bytes());
70 } else {
71 out.extend_from_slice(format!("*{}\r\n", fields.len() * 2).as_bytes());
72 }
73 for (k, v) in fields {
74 out.extend_from_slice(&super::actions::encode_bulk_string(k.as_bytes()));
75 match v {
76 Value::String(s) => {
77 out.extend_from_slice(&super::actions::encode_bulk_string(s.as_bytes()))
78 }
79 Value::Number(n) => {
80 out.extend_from_slice(&super::actions::encode_integer(n.as_i64().unwrap_or(0)))
81 }
82 _ => out.extend_from_slice(b"*0\r\n"),
83 }
84 }
85 out
86}
87
88pub const REPORTED_VERSION: &str = "7.4.0";
91
92pub fn null(proto: u8) -> Vec<u8> {
94 if proto == 3 {
95 b"_\r\n".to_vec()
96 } else {
97 super::actions::encode_null()
98 }
99}
100
101fn double_text(v: &Value) -> Result<String> {
103 match v {
104 Value::Number(n) => {
105 let f = n.as_f64().context("not a number")?;
106 Ok(if f.fract() == 0.0 && f.abs() < 1e15 {
107 format!("{f:.0}")
108 } else {
109 n.to_string()
110 })
111 }
112 Value::String(s) if matches!(s.as_str(), "inf" | "-inf" | "nan") => Ok(s.clone()),
113 Value::String(s) => {
114 let f: f64 = s.parse().context("value is a number, inf, -inf or nan")?;
115 ensure!(f.is_finite(), "value is a number, inf, -inf or nan");
116 Ok(s.clone())
117 }
118 _ => bail!("value is a number, inf, -inf or nan"),
119 }
120}
121
122pub fn double(v: &Value, proto: u8) -> Result<Vec<u8>> {
123 let text = double_text(v)?;
124 Ok(if proto == 3 {
125 format!(",{text}\r\n").into_bytes()
126 } else {
127 super::actions::encode_bulk_string(text.as_bytes())
128 })
129}
130
131pub fn boolean(b: bool, proto: u8) -> Vec<u8> {
132 if proto == 3 {
133 if b {
134 b"#t\r\n".to_vec()
135 } else {
136 b"#f\r\n".to_vec()
137 }
138 } else {
139 super::actions::encode_integer(i64::from(b))
140 }
141}
142
143pub fn big_number(digits: &str, proto: u8) -> Result<Vec<u8>> {
144 let body = digits.strip_prefix('-').unwrap_or(digits);
145 ensure!(
146 !body.is_empty() && body.len() <= 4096 && body.bytes().all(|b| b.is_ascii_digit()),
147 "value is a decimal integer, optionally negative"
148 );
149 Ok(if proto == 3 {
150 format!("({digits}\r\n").into_bytes()
151 } else {
152 super::actions::encode_bulk_string(digits.as_bytes())
153 })
154}
155
156pub fn verbatim(format: &str, text: &str, proto: u8) -> Result<Vec<u8>> {
157 ensure!(
158 format.len() == 3 && format.bytes().all(|b| b.is_ascii_alphanumeric()),
159 "format is three letters, such as txt or mkd"
160 );
161 Ok(if proto == 3 {
162 let body = format!("{format}:{text}");
163 let mut out = format!("={}\r\n", body.len()).into_bytes();
164 out.extend_from_slice(body.as_bytes());
165 out.extend_from_slice(b"\r\n");
166 out
167 } else {
168 super::actions::encode_bulk_string(text.as_bytes())
169 })
170}
171
172pub fn value(v: &Value, depth: usize) -> Result<Vec<u8>> {
176 ensure!(
177 depth <= MAX_REPLY_DEPTH,
178 "the reply nests deeper than {MAX_REPLY_DEPTH} levels"
179 );
180 Ok(match v {
181 Value::String(s) => super::actions::encode_bulk_string(s.as_bytes()),
182 Value::Number(n) => match n.as_i64() {
183 Some(i) => super::actions::encode_integer(i),
184 None => double(v, 3)?,
185 },
186 Value::Bool(b) => boolean(*b, 3),
187 Value::Null => null(3),
188 Value::Array(items) => aggregate(b'*', items, depth)?,
189 Value::Object(map) => {
190 let mut out = format!("%{}\r\n", map.len()).into_bytes();
191 for (k, v) in map {
192 out.extend_from_slice(&super::actions::encode_bulk_string(k.as_bytes()));
193 out.extend_from_slice(&value(v, depth + 1)?);
194 }
195 out
196 }
197 })
198}
199
200fn aggregate(kind: u8, items: &[Value], depth: usize) -> Result<Vec<u8>> {
201 let mut out = vec![kind];
202 out.extend_from_slice(format!("{}\r\n", items.len()).as_bytes());
203 for item in items {
204 out.extend_from_slice(&value(item, depth + 1)?);
205 }
206 Ok(out)
207}
208
209pub fn entries(v: &Value) -> Result<Vec<(Value, Value)>> {
211 match v {
212 Value::Object(map) => Ok(map
213 .iter()
214 .map(|(k, v)| (Value::String(k.clone()), v.clone()))
215 .collect()),
216 Value::Array(pairs) => pairs
217 .iter()
218 .map(|p| match p.as_array().map(Vec::as_slice) {
219 Some([k, v]) => Ok((k.clone(), v.clone())),
220 _ => bail!("each entry is a [key, value] pair"),
221 })
222 .collect(),
223 _ => bail!("entries is an object or an array of [key, value] pairs"),
224 }
225}
226
227pub fn map(entries: &[(Value, Value)], proto: u8) -> Result<Vec<u8>> {
229 if proto == 3 {
230 let mut out = format!("%{}\r\n", entries.len()).into_bytes();
231 for (k, v) in entries {
232 out.extend_from_slice(&value(k, 1)?);
233 out.extend_from_slice(&value(v, 1)?);
234 }
235 Ok(out)
236 } else {
237 let flat: Vec<Value> = entries
238 .iter()
239 .flat_map(|(k, v)| [k.clone(), v.clone()])
240 .collect();
241 Ok(super::actions::encode_array(&flat))
242 }
243}
244
245pub fn set(items: &[Value], proto: u8) -> Result<Vec<u8>> {
247 if proto == 3 {
248 aggregate(b'~', items, 0)
249 } else {
250 Ok(super::actions::encode_array(items))
251 }
252}
253
254pub fn array(items: &[Value], proto: u8) -> Result<Vec<u8>> {
256 if proto == 3 {
257 aggregate(b'*', items, 0)
258 } else {
259 Ok(super::actions::encode_array(items))
260 }
261}
262
263pub fn push(items: &[Value], proto: u8) -> Result<Vec<u8>> {
265 ensure!(
266 proto == 3,
267 "this connection speaks RESP2, which has no push frame; a client must send HELLO 3 first"
268 );
269 ensure!(
270 !items.is_empty(),
271 "a push names its kind as its first element"
272 );
273 aggregate(b'>', items, 0)
274}