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.commands.last_mut().unwrap().args.push(data.as_ref());
self
}
pub fn args<D>(mut self, arguments: &'a [D]) -> Self
where
D: AsRef<[u8]>,
{
let last_command = self.commands.last_mut().unwrap();
for arg in arguments {
last_command.args.push(arg.as_ref());
}
self
}
pub fn command(mut self, cmd: &'a str) -> Self {
self.commands.push(Command::new(cmd));
self
}
pub fn command_count(&self) -> usize {
self.commands.len()
}
pub(crate) fn serialize(self) -> Vec<u8> {
let mut out = Vec::new();
for command in self.commands {
let mut serialized = command.serialize();
out.append(&mut serialized);
}
out
}
}
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.args.push(data.as_ref());
self
}
pub fn args<D>(mut self, arguments: &'a [D]) -> Self
where
D: AsRef<[u8]>,
{
for arg in arguments {
self.args.push(arg.as_ref());
}
self
}
pub(crate) fn serialize(self) -> Vec<u8> {
let mut out = format!("*{}\r\n", self.args.len() + 1).into_bytes();
let mut serialized_command =
format!("${}\r\n{}\r\n", self.command.len(), self.command).into_bytes();
out.append(&mut serialized_command);
for arg in self.args {
let mut serialized = format!("${}\r\n", arg.len()).into_bytes();
for byte in arg {
serialized.push(*byte);
}
serialized.push(b'\r');
serialized.push(b'\n');
out.append(&mut serialized);
}
out
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn serialize_singular() {
let command = Command::new("GET").arg(b"some-key").serialize();
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 command = CommandList::new("GET")
.arg(b"some-key")
.command("LLEN")
.arg(b"some-other-key")
.serialize();
assert_eq!(
String::from_utf8_lossy(&command),
"*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();
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"
);
}
}