pub fn encode_map_header(out: &mut Vec<u8>, count: i64) {
out.push(b'%');
push_int(out, count);
out.extend_from_slice(b"\r\n");
}
pub fn encode_set_header(out: &mut Vec<u8>, count: i64) {
out.push(b'~');
push_int(out, count);
out.extend_from_slice(b"\r\n");
}
pub fn encode_push_header(out: &mut Vec<u8>, count: i64) {
out.push(b'>');
push_int(out, count);
out.extend_from_slice(b"\r\n");
}
pub fn encode_double(out: &mut Vec<u8>, v: f64) {
out.push(b',');
if v.is_nan() {
out.extend_from_slice(b"nan");
} else if v.is_infinite() {
out.extend_from_slice(if v > 0.0 { b"inf" } else { b"-inf" });
} else {
if v == v.trunc() && v.abs() < 1e17 {
push_int(out, v as i64);
} else {
use std::io::Write as _;
let _ = write!(out, "{v}");
}
}
out.extend_from_slice(b"\r\n");
}
pub fn encode_boolean(out: &mut Vec<u8>, v: bool) {
out.extend_from_slice(if v { b"#t\r\n" } else { b"#f\r\n" });
}
pub fn encode_null(out: &mut Vec<u8>) {
out.extend_from_slice(b"_\r\n");
}
pub fn encode_big_number(out: &mut Vec<u8>, digits: &[u8]) {
out.reserve(digits.len() + 4);
out.push(b'(');
out.extend_from_slice(digits);
out.extend_from_slice(b"\r\n");
}
pub fn encode_verbatim(out: &mut Vec<u8>, fmt: [u8; 3], data: &[u8]) {
let total_len = 4 + data.len();
out.reserve(total_len + 16);
out.push(b'=');
push_int(out, total_len as i64);
out.extend_from_slice(b"\r\n");
out.extend_from_slice(&fmt);
out.push(b':');
out.extend_from_slice(data);
out.extend_from_slice(b"\r\n");
}
pub fn encode_blob_error(out: &mut Vec<u8>, msg: &[u8]) {
out.reserve(msg.len() + 16);
out.push(b'!');
push_int(out, msg.len() as i64);
out.extend_from_slice(b"\r\n");
out.extend_from_slice(msg);
out.extend_from_slice(b"\r\n");
}
fn push_int(out: &mut Vec<u8>, n: i64) {
if n == 0 {
out.push(b'0');
return;
}
let mut tmp = [0u8; 20];
let mut i = tmp.len();
let neg = n < 0;
let mut v = n;
while v != 0 {
let digit = (v % 10).unsigned_abs() as u8;
i -= 1;
tmp[i] = b'0' + digit;
v /= 10;
}
if neg {
out.push(b'-');
}
out.extend_from_slice(&tmp[i..]);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{parse_reply, Reply};
#[test]
fn map_header_round_trip() {
let mut out = Vec::new();
encode_map_header(&mut out, 2);
crate::encode_integer(&mut out, 1);
crate::encode_bulk(&mut out, b"a");
crate::encode_integer(&mut out, 2);
crate::encode_bulk(&mut out, b"b");
assert_eq!(out, b"%2\r\n:1\r\n$1\r\na\r\n:2\r\n$1\r\nb\r\n");
let (r, used) = parse_reply(&out).unwrap().unwrap();
assert_eq!(used, out.len());
assert_eq!(
r,
Reply::Map(vec![
(Reply::Int(1), Reply::Bulk(b"a".to_vec())),
(Reply::Int(2), Reply::Bulk(b"b".to_vec())),
])
);
}
#[test]
fn set_header_round_trip() {
let mut out = Vec::new();
encode_set_header(&mut out, 3);
for i in 1..=3 {
crate::encode_integer(&mut out, i);
}
assert_eq!(out, b"~3\r\n:1\r\n:2\r\n:3\r\n");
let (r, _) = parse_reply(&out).unwrap().unwrap();
assert_eq!(r, Reply::Set(vec![Reply::Int(1), Reply::Int(2), Reply::Int(3)]));
}
#[test]
fn push_header_round_trip() {
let mut out = Vec::new();
encode_push_header(&mut out, 3);
crate::encode_simple_string(&mut out, "message");
crate::encode_bulk(&mut out, b"news");
crate::encode_bulk(&mut out, b"hello");
assert_eq!(out, b">3\r\n+message\r\n$4\r\nnews\r\n$5\r\nhello\r\n");
let (r, _) = parse_reply(&out).unwrap().unwrap();
assert_eq!(
r,
Reply::Push(vec![
Reply::Simple(b"message".to_vec()),
Reply::Bulk(b"news".to_vec()),
Reply::Bulk(b"hello".to_vec()),
])
);
}
#[test]
fn double_round_trip() {
let mut out = Vec::new();
encode_double(&mut out, 1.5);
assert_eq!(out, b",1.5\r\n");
let (r, _) = parse_reply(&out).unwrap().unwrap();
assert_eq!(r, Reply::Double(1.5));
out.clear();
encode_double(&mut out, 5.0); assert_eq!(out, b",5\r\n");
let (r, _) = parse_reply(&out).unwrap().unwrap();
assert_eq!(r, Reply::Double(5.0));
out.clear();
encode_double(&mut out, f64::INFINITY);
assert_eq!(out, b",inf\r\n");
out.clear();
encode_double(&mut out, f64::NEG_INFINITY);
assert_eq!(out, b",-inf\r\n");
out.clear();
encode_double(&mut out, f64::NAN);
assert_eq!(out, b",nan\r\n");
}
#[test]
fn boolean_and_null_round_trip() {
let mut out = Vec::new();
encode_boolean(&mut out, true);
assert_eq!(out, b"#t\r\n");
let (r, _) = parse_reply(&out).unwrap().unwrap();
assert_eq!(r, Reply::Boolean(true));
out.clear();
encode_boolean(&mut out, false);
assert_eq!(out, b"#f\r\n");
let (r, _) = parse_reply(&out).unwrap().unwrap();
assert_eq!(r, Reply::Boolean(false));
out.clear();
encode_null(&mut out);
assert_eq!(out, b"_\r\n");
let (r, _) = parse_reply(&out).unwrap().unwrap();
assert_eq!(r, Reply::Null);
}
#[test]
fn verbatim_round_trip() {
let mut out = Vec::new();
encode_verbatim(&mut out, *b"txt", b"Some string");
assert_eq!(out, b"=15\r\ntxt:Some string\r\n");
let (r, _) = parse_reply(&out).unwrap().unwrap();
assert_eq!(r, Reply::Verbatim { fmt: *b"txt", data: b"Some string".to_vec() });
}
#[test]
fn big_number_round_trip() {
let mut out = Vec::new();
encode_big_number(&mut out, b"170141183460469231731687303715884105727");
assert_eq!(out, b"(170141183460469231731687303715884105727\r\n");
let (r, _) = parse_reply(&out).unwrap().unwrap();
assert_eq!(
r,
Reply::BigNumber(b"170141183460469231731687303715884105727".to_vec())
);
}
#[test]
fn blob_error_round_trip() {
let mut out = Vec::new();
encode_blob_error(&mut out, b"ERR bad thing\nwith newline");
let (r, _) = parse_reply(&out).unwrap().unwrap();
assert_eq!(r, Reply::BlobError(b"ERR bad thing\nwith newline".to_vec()));
}
}