1pub fn encode_map_header(out: &mut Vec<u8>, count: i64) {
25 out.push(b'%');
26 push_int(out, count);
27 out.extend_from_slice(b"\r\n");
28}
29
30pub fn encode_set_header(out: &mut Vec<u8>, count: i64) {
36 out.push(b'~');
37 push_int(out, count);
38 out.extend_from_slice(b"\r\n");
39}
40
41pub fn encode_push_header(out: &mut Vec<u8>, count: i64) {
46 out.push(b'>');
47 push_int(out, count);
48 out.extend_from_slice(b"\r\n");
49}
50
51pub fn encode_double(out: &mut Vec<u8>, v: f64) {
60 out.push(b',');
61 if v.is_nan() {
62 out.extend_from_slice(b"nan");
63 } else if v.is_infinite() {
64 out.extend_from_slice(if v > 0.0 { b"inf" } else { b"-inf" });
65 } else {
66 #[allow(clippy::float_cmp)]
72 let is_integer_valued = v == v.trunc();
73 if is_integer_valued && v.abs() < 1e17 {
74 push_int(out, v as i64);
75 } else {
76 use std::io::Write as _;
80 let _ = write!(out, "{v}");
81 }
82 }
83 out.extend_from_slice(b"\r\n");
84}
85
86pub fn encode_boolean(out: &mut Vec<u8>, v: bool) {
88 out.extend_from_slice(if v { b"#t\r\n" } else { b"#f\r\n" });
89}
90
91pub fn encode_null(out: &mut Vec<u8>) {
94 out.extend_from_slice(b"_\r\n");
95}
96
97pub fn encode_big_number(out: &mut Vec<u8>, digits: &[u8]) {
101 out.reserve(digits.len() + 4);
102 out.push(b'(');
103 out.extend_from_slice(digits);
104 out.extend_from_slice(b"\r\n");
105}
106
107pub fn encode_verbatim(out: &mut Vec<u8>, fmt: [u8; 3], data: &[u8]) {
116 let total_len = 4 + data.len();
117 out.reserve(total_len + 16);
118 out.push(b'=');
119 push_int(out, total_len as i64);
120 out.extend_from_slice(b"\r\n");
121 out.extend_from_slice(&fmt);
122 out.push(b':');
123 out.extend_from_slice(data);
124 out.extend_from_slice(b"\r\n");
125}
126
127pub fn encode_blob_error(out: &mut Vec<u8>, msg: &[u8]) {
130 out.reserve(msg.len() + 16);
131 out.push(b'!');
132 push_int(out, msg.len() as i64);
133 out.extend_from_slice(b"\r\n");
134 out.extend_from_slice(msg);
135 out.extend_from_slice(b"\r\n");
136}
137
138fn push_int(out: &mut Vec<u8>, n: i64) {
142 if n == 0 {
143 out.push(b'0');
144 return;
145 }
146 let mut tmp = [0u8; 20];
147 let mut i = tmp.len();
148 let neg = n < 0;
149 let mut v = n;
150 while v != 0 {
151 let digit = (v % 10).unsigned_abs() as u8;
152 i -= 1;
153 tmp[i] = b'0' + digit;
154 v /= 10;
155 }
156 if neg {
157 out.push(b'-');
158 }
159 out.extend_from_slice(&tmp[i..]);
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165 use crate::{parse_reply, Reply};
166
167 #[test]
170 fn map_header_round_trip() {
171 let mut out = Vec::new();
172 encode_map_header(&mut out, 2);
173 crate::encode_integer(&mut out, 1);
175 crate::encode_bulk(&mut out, b"a");
176 crate::encode_integer(&mut out, 2);
177 crate::encode_bulk(&mut out, b"b");
178 assert_eq!(out, b"%2\r\n:1\r\n$1\r\na\r\n:2\r\n$1\r\nb\r\n");
179 let (r, used) = parse_reply(&out).unwrap().unwrap();
180 assert_eq!(used, out.len());
181 assert_eq!(
182 r,
183 Reply::Map(vec![
184 (Reply::Int(1), Reply::Bulk(b"a".to_vec())),
185 (Reply::Int(2), Reply::Bulk(b"b".to_vec())),
186 ])
187 );
188 }
189
190 #[test]
191 fn set_header_round_trip() {
192 let mut out = Vec::new();
193 encode_set_header(&mut out, 3);
194 for i in 1..=3 {
195 crate::encode_integer(&mut out, i);
196 }
197 assert_eq!(out, b"~3\r\n:1\r\n:2\r\n:3\r\n");
198 let (r, _) = parse_reply(&out).unwrap().unwrap();
199 assert_eq!(r, Reply::Set(vec![Reply::Int(1), Reply::Int(2), Reply::Int(3)]));
200 }
201
202 #[test]
203 fn push_header_round_trip() {
204 let mut out = Vec::new();
205 encode_push_header(&mut out, 3);
206 crate::encode_simple_string(&mut out, "message");
207 crate::encode_bulk(&mut out, b"news");
208 crate::encode_bulk(&mut out, b"hello");
209 assert_eq!(out, b">3\r\n+message\r\n$4\r\nnews\r\n$5\r\nhello\r\n");
210 let (r, _) = parse_reply(&out).unwrap().unwrap();
211 assert_eq!(
212 r,
213 Reply::Push(vec![
214 Reply::Simple(b"message".to_vec()),
215 Reply::Bulk(b"news".to_vec()),
216 Reply::Bulk(b"hello".to_vec()),
217 ])
218 );
219 }
220
221 #[test]
222 fn double_round_trip() {
223 let mut out = Vec::new();
224 encode_double(&mut out, 1.5);
226 assert_eq!(out, b",1.5\r\n");
227 let (r, _) = parse_reply(&out).unwrap().unwrap();
228 assert_eq!(r, Reply::Double(1.5));
229
230 out.clear();
231 encode_double(&mut out, 5.0); assert_eq!(out, b",5\r\n");
233 let (r, _) = parse_reply(&out).unwrap().unwrap();
234 assert_eq!(r, Reply::Double(5.0));
235
236 out.clear();
237 encode_double(&mut out, f64::INFINITY);
238 assert_eq!(out, b",inf\r\n");
239
240 out.clear();
241 encode_double(&mut out, f64::NEG_INFINITY);
242 assert_eq!(out, b",-inf\r\n");
243
244 out.clear();
245 encode_double(&mut out, f64::NAN);
246 assert_eq!(out, b",nan\r\n");
247 }
248
249 #[test]
250 fn boolean_and_null_round_trip() {
251 let mut out = Vec::new();
252 encode_boolean(&mut out, true);
253 assert_eq!(out, b"#t\r\n");
254 let (r, _) = parse_reply(&out).unwrap().unwrap();
255 assert_eq!(r, Reply::Boolean(true));
256
257 out.clear();
258 encode_boolean(&mut out, false);
259 assert_eq!(out, b"#f\r\n");
260 let (r, _) = parse_reply(&out).unwrap().unwrap();
261 assert_eq!(r, Reply::Boolean(false));
262
263 out.clear();
264 encode_null(&mut out);
265 assert_eq!(out, b"_\r\n");
266 let (r, _) = parse_reply(&out).unwrap().unwrap();
267 assert_eq!(r, Reply::Null);
268 }
269
270 #[test]
271 fn verbatim_round_trip() {
272 let mut out = Vec::new();
273 encode_verbatim(&mut out, *b"txt", b"Some string");
274 assert_eq!(out, b"=15\r\ntxt:Some string\r\n");
275 let (r, _) = parse_reply(&out).unwrap().unwrap();
276 assert_eq!(r, Reply::Verbatim { fmt: *b"txt", data: b"Some string".to_vec() });
277 }
278
279 #[test]
280 fn big_number_round_trip() {
281 let mut out = Vec::new();
282 encode_big_number(&mut out, b"170141183460469231731687303715884105727");
283 assert_eq!(out, b"(170141183460469231731687303715884105727\r\n");
284 let (r, _) = parse_reply(&out).unwrap().unwrap();
285 assert_eq!(
286 r,
287 Reply::BigNumber(b"170141183460469231731687303715884105727".to_vec())
288 );
289 }
290
291 #[test]
292 fn blob_error_round_trip() {
293 let mut out = Vec::new();
294 encode_blob_error(&mut out, b"ERR bad thing\nwith newline");
295 let (r, _) = parse_reply(&out).unwrap().unwrap();
296 assert_eq!(r, Reply::BlobError(b"ERR bad thing\nwith newline".to_vec()));
297 }
298}