use crate::connection::builder::MSetBuilder;
use std::io::Write;
#[derive(Clone, Debug)]
pub struct CommandList<'a> {
commands: Vec<Command<'a>>,
}
impl<'a> CommandList<'a> {
pub fn new(cmd: &'a str) -> Self {
let commands = vec![Command::new(cmd)];
Self { commands }
}
pub fn arg<D>(mut self, data: &'a D) -> Self
where
D: AsRef<[u8]>,
{
self.append_arg(data);
self
}
pub fn args<D>(mut self, arguments: &'a [D]) -> Self
where
D: AsRef<[u8]>,
{
self.append_args(arguments);
self
}
pub fn command(mut self, cmd: &'a str) -> Self {
self.commands.push(Command::new(cmd));
self
}
pub fn append_args<D>(&mut self, arguments: &'a [D])
where
D: AsRef<[u8]>,
{
let last_command = self.commands.last_mut().unwrap();
for arg in arguments {
last_command.args.push(arg.as_ref());
}
}
pub fn append_arg<D>(&mut self, data: &'a D)
where
D: AsRef<[u8]>,
{
self.commands.last_mut().unwrap().args.push(data.as_ref());
}
pub fn append_command(&mut self, cmd: &'a str) {
self.commands.push(Command::new(cmd))
}
pub fn command_count(&self) -> usize {
self.commands.len()
}
pub(crate) fn serialize(self, buffer: &mut Vec<u8>) {
for command in self.commands {
command.serialize(buffer);
}
}
}
#[cfg(feature = "bench")]
impl<'a> CommandList<'a> {
#[inline(always)]
pub fn serialize_bench(self) -> Vec<u8> {
self.serialize()
}
}
#[derive(Clone, Debug)]
pub struct Command<'a> {
command: &'a str,
args: Vec<&'a [u8]>,
}
impl<'a> Command<'a> {
pub fn new(cmd: &'a str) -> Self {
Self {
command: cmd,
args: Vec::new(),
}
}
pub fn arg<D>(mut self, data: &'a D) -> Self
where
D: AsRef<[u8]>,
{
self.append_arg(data);
self
}
pub fn args<D>(mut self, arguments: &'a [D]) -> Self
where
D: AsRef<[u8]>,
{
self.append_args(arguments);
self
}
pub fn append_arg<D>(&mut self, data: &'a D)
where
D: AsRef<[u8]>,
{
self.args.push(data.as_ref());
}
pub fn append_args<D>(&mut self, arguments: &'a [D])
where
D: AsRef<[u8]>,
{
for arg in arguments {
self.args.push(arg.as_ref());
}
}
pub(crate) fn serialize(self, buffer: &mut Vec<u8>) {
write!(
buffer,
"*{}\r\n${}\r\n{}\r\n",
self.args.len() + 1,
self.command.len(),
self.command
)
.unwrap();
for arg in self.args {
write!(buffer, "${}\r\n", arg.len()).unwrap();
for byte in arg {
buffer.push(*byte);
}
buffer.push(b'\r');
buffer.push(b'\n');
}
}
pub(crate) fn append_msetbuilder(&mut self, builder: &'a MSetBuilder<'a>) {
for item in builder.build() {
self.args.push(*item);
}
}
}
#[cfg(any(feature = "bench", test))]
impl<'a> Command<'a> {
#[inline(always)]
pub fn serialize_bench(self) -> Vec<u8> {
let mut out = Vec::new();
self.serialize(&mut out);
out
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn serialize_singular() {
let command = Command::new("GET").arg(b"some-key").serialize_bench();
assert_eq!(
String::from_utf8_lossy(&command),
"*2\r\n$3\r\nGET\r\n$8\r\nsome-key\r\n"
)
}
#[test]
fn serialize_multiple() {
let mut buf = Vec::new();
CommandList::new("GET")
.arg(b"some-key")
.command("LLEN")
.arg(b"some-other-key")
.serialize(&mut buf);
assert_eq!(
String::from_utf8_lossy(&buf),
"*2\r\n$3\r\nGET\r\n$8\r\nsome-key\r\n*2\r\n$4\r\nLLEN\r\n$14\r\nsome-other-key\r\n"
);
}
#[test]
fn multiple_args() {
let arguments = vec!["a", "b", "c"];
let command = Command::new("LPUSH")
.arg(b"some-key")
.args(&arguments)
.serialize_bench();
assert_eq!(
String::from_utf8_lossy(&command),
"*5\r\n$5\r\nLPUSH\r\n$8\r\nsome-key\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
);
}
}