use core::str;
use wbase::num::strict_i64;
use crate::{Resp2, Resp3, RespProtocol, RespWriter};
pub const MIN_ERROR_PREFIX_LEN: usize = 3;
pub const MAX_ERROR_MSG_LEN: usize = 512;
#[inline]
pub fn sanitize_error_str(s: &str, max_len: usize) -> &str {
let truncated = match s.find(['\r', '\n']) {
Some(idx) => &s[..idx],
None => s,
};
if truncated.len() <= max_len {
truncated
} else {
let boundary = truncated.floor_char_boundary(max_len);
&truncated[..boundary]
}
}
pub trait RespSliceExt {
fn as_str_safe(&self) -> &str;
fn try_parse_i64(&self) -> Option<i64>;
}
impl RespSliceExt for [u8] {
#[inline]
fn as_str_safe(&self) -> &str {
str::from_utf8(self).unwrap_or("")
}
#[inline]
fn try_parse_i64(&self) -> Option<i64> {
strict_i64(self)
}
}
pub trait RespVecExt {
fn write_resp_int(&mut self, val: i64);
fn write_resp_bulk_string(&mut self, val: &[u8]);
fn write_resp_array_len(&mut self, len: usize);
fn write_resp_error(&mut self, msg: &str);
fn write_resp_simple_string(&mut self, msg: &str);
fn write_resp_null(&mut self);
fn resp_writer<P: RespProtocol>(&mut self) -> RespWriter<&mut Vec<u8>, P>;
fn resp_writer2(&mut self) -> RespWriter<&mut Vec<u8>, Resp2>;
fn resp_writer3(&mut self) -> RespWriter<&mut Vec<u8>, Resp3>;
}
impl RespVecExt for Vec<u8> {
#[inline]
fn write_resp_int(&mut self, val: i64) {
RespWriter::new_ref(self).write_int64(val);
}
#[inline]
fn write_resp_bulk_string(&mut self, val: &[u8]) {
RespWriter::new_ref(self).write_bulk_string(val);
}
#[inline]
fn write_resp_array_len(&mut self, len: usize) {
RespWriter::new_ref(self).write_array_length(len);
}
#[inline]
fn write_resp_error(&mut self, msg: &str) {
let mut writer = RespWriter::new_ref(self);
if let Some((prefix, _)) = msg.split_once(' ')
&& prefix.len() >= MIN_ERROR_PREFIX_LEN
&& prefix.bytes().all(|b| b.is_ascii_uppercase())
{
writer.write_error(msg);
} else if msg == "ERR" {
writer.write_error("ERR");
} else {
writer.write_error_with_prefix("ERR", msg);
}
}
#[inline]
fn write_resp_simple_string(&mut self, msg: &str) {
RespWriter::new_ref(self).write_simple_string(msg);
}
#[inline]
fn write_resp_null(&mut self) {
RespWriter::new_ref(self).write_null();
}
#[inline]
fn resp_writer<P: RespProtocol>(&mut self) -> RespWriter<&mut Vec<u8>, P> {
RespWriter::new_ref_p(self)
}
#[inline]
fn resp_writer2(&mut self) -> RespWriter<&mut Vec<u8>, Resp2> {
RespWriter::new_ref(self)
}
#[inline]
fn resp_writer3(&mut self) -> RespWriter<&mut Vec<u8>, Resp3> {
RespWriter::new_ref_p(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn try_parse_i64_strict() {
assert_eq!(b"42".try_parse_i64(), Some(42));
assert_eq!(b"-7".try_parse_i64(), Some(-7));
assert_eq!(b"+3".try_parse_i64(), Some(3));
assert_eq!(b"0".try_parse_i64(), Some(0));
assert_eq!(b"-0".try_parse_i64(), Some(0));
assert_eq!(b"9223372036854775807".try_parse_i64(), Some(i64::MAX));
assert_eq!(b"-9223372036854775808".try_parse_i64(), Some(i64::MIN));
assert_eq!(b"007".try_parse_i64(), None);
assert_eq!(b"-007".try_parse_i64(), None);
assert_eq!(b"abc".try_parse_i64(), None);
assert_eq!(b"".try_parse_i64(), None);
assert_eq!(b"1 2".try_parse_i64(), None);
assert_eq!(b" 1".try_parse_i64(), None);
assert_eq!(b"5 ".try_parse_i64(), None);
assert_eq!(b"1x".try_parse_i64(), None);
assert_eq!(b"9223372036854775808".try_parse_i64(), None);
assert_eq!(b"-9223372036854775809".try_parse_i64(), None);
}
#[test]
fn vec_ext_formatting() {
let mut buf = Vec::new();
buf.write_resp_int(100);
buf.write_resp_simple_string("OK");
buf.write_resp_bulk_string(b"foo");
buf.write_resp_array_len(2);
buf.write_resp_null();
buf.write_resp_error("something failed");
assert_eq!(
buf,
b":100\r\n+OK\r\n$3\r\nfoo\r\n*2\r\n$-1\r\n-ERR something failed\r\n"
);
let mut buf2 = Vec::new();
buf2.write_resp_error("ERR already has prefix");
assert_eq!(buf2, b"-ERR already has prefix\r\n");
let mut buf3 = Vec::new();
buf3.write_resp_error("WRONGTYPE Operation against a key holding the wrong kind of value.");
assert_eq!(
buf3,
b"-WRONGTYPE Operation against a key holding the wrong kind of value.\r\n"
);
}
}