use std::io;
use kevy_resp::{Reply, encode_command_borrowed};
use crate::Connection;
#[derive(Debug, Default)]
pub struct PipelineBuf {
buf: Vec<u8>,
count: usize,
poisoned: bool,
}
impl PipelineBuf {
pub fn cmd(&mut self, parts: &[&[u8]]) -> &mut Self {
if parts.is_empty() {
self.poisoned = true;
return self;
}
encode_command_borrowed(&mut self.buf, parts);
self.count += 1;
self
}
pub fn len(&self) -> usize {
self.count
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
}
impl Connection {
pub fn pipeline(
&mut self,
build: impl FnOnce(&mut PipelineBuf),
) -> io::Result<Vec<Reply>> {
let client = self.remote("pipeline")?;
let mut p = PipelineBuf::default();
build(&mut p);
if p.poisoned {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"pipeline: a queued command had an empty argv",
));
}
if p.count == 0 {
return Ok(Vec::new());
}
client.pipeline_raw(&p.buf, p.count)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn embedded_pipeline_unsupported() {
let mut c = Connection::open("mem://").unwrap();
let err = c.pipeline(|p| {
p.cmd(&[b"PING"]);
});
assert_eq!(err.unwrap_err().kind(), io::ErrorKind::Unsupported);
}
#[test]
fn pipeline_buf_counts_and_encodes() {
let mut p = PipelineBuf::default();
assert!(p.is_empty());
p.cmd(&[b"SET", b"a", b"1"]).cmd(&[b"GET", b"a"]);
assert_eq!(p.len(), 2);
assert!(p.buf.starts_with(b"*3\r\n$3\r\nSET\r\n"));
}
}