use super::Connection;
use crate::Command;
use futures::{
task::{Context, Poll},
FutureExt, Stream,
};
use std::{collections::VecDeque, future::Future, iter::Iterator, pin::Pin};
#[derive(Debug)]
pub struct ScanBuilder<'a> {
connection: &'a mut Connection,
pattern: Option<&'a [u8]>,
key: Option<&'a [u8]>,
count: Option<isize>,
command: &'static str,
}
impl<'a> ScanBuilder<'a> {
pub(crate) fn new(
command: &'static str,
key: Option<&'a [u8]>,
connection: &'a mut Connection,
) -> Self {
Self {
connection,
key,
command,
pattern: None,
count: None,
}
}
pub fn pattern<P>(mut self, pattern: &'a P) -> Self
where
P: AsRef<[u8]>,
{
self.pattern = Some(pattern.as_ref());
self
}
pub fn count(mut self, count: usize) -> Self {
self.count = Some(count as isize);
self
}
pub fn run(self) -> ScanStream<'a> {
ScanStream::new(
self.command,
self.key,
self.pattern,
self.count,
self.connection,
)
}
}
type ScanStreamFuture<'a> = Pin<Box<dyn Future<Output = (Vec<u8>, Vec<Vec<u8>>)> + Send + 'a>>;
#[must_use]
#[allow(missing_debug_implementations)]
pub struct ScanStream<'a> {
command: &'static str,
key: Option<&'a [u8]>,
pattern: Option<&'a [u8]>,
count: Option<isize>,
connection: Connection,
poll_future: ScanStreamFuture<'a>,
last_cursor: Vec<u8>,
receive_buffer: VecDeque<Vec<u8>>,
}
impl<'a> ScanStream<'a> {
pub(crate) fn new(
command: &'static str,
key: Option<&'a [u8]>,
pattern: Option<&'a [u8]>,
count: Option<isize>,
connection: &'a mut Connection,
) -> Self {
let connection = connection.clone();
let poll_future = Self::create_poll_future(
command,
key,
b"0".to_vec(), pattern,
count,
connection.clone(),
);
let receive_buffer = VecDeque::new();
Self {
command,
connection,
count,
key,
last_cursor: b"1".to_vec(), pattern,
poll_future,
receive_buffer,
}
}
fn create_poll_future(
command: &'static str,
key: Option<&'a [u8]>,
cursor: Vec<u8>,
pattern: Option<&'a [u8]>,
count: Option<isize>,
mut conn: Connection,
) -> ScanStreamFuture<'a> {
async move {
let mut command = Command::new(command);
if let Some(ref key) = key {
command.append_arg(key);
}
command.append_arg(&cursor);
if let Some(ref pattern) = pattern {
command.append_arg(b"MATCH");
command.append_arg(pattern);
}
let count = count.clone().map(|s| s.to_string()); if let Some(ref count) = count {
command.append_arg(b"COUNT");
command.append_arg(count);
}
let mut result = conn
.run_command(command)
.await
.expect("Running SCAN command")
.unwrap_array()
.into_iter();
let cursor = result.next().expect("getting cursor field").unwrap_string();
let values = result
.next()
.expect("getting value array")
.unwrap_array()
.into_iter()
.map(|s| s.unwrap_string())
.collect();
(cursor, values)
}
.boxed()
}
}
impl<'a> Stream for ScanStream<'a> {
type Item = Vec<u8>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
if let Some(v) = self.receive_buffer.pop_front() {
Poll::Ready(Some(v))
} else if self.last_cursor == b"0" {
Poll::Ready(None)
} else {
match self.poll_future.as_mut().poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready((cursor, fields)) => {
self.poll_future = Self::create_poll_future(
self.command,
self.key,
cursor.clone(),
self.pattern,
self.count,
self.connection.clone(),
);
for f in fields {
self.receive_buffer.push_back(f);
}
self.last_cursor = cursor;
Poll::Ready(self.receive_buffer.pop_front())
}
}
}
}
}
#[derive(Debug)]
pub struct HScanBuilder<'a> {
connection: &'a mut Connection,
pattern: Option<&'a [u8]>,
key: &'a [u8],
count: Option<isize>,
}
impl<'a> HScanBuilder<'a> {
pub(crate) fn new(key: &'a [u8], connection: &'a mut Connection) -> Self {
Self {
connection,
key,
pattern: None,
count: None,
}
}
pub fn pattern<P>(mut self, pattern: &'a P) -> Self
where
P: AsRef<[u8]>,
{
self.pattern = Some(pattern.as_ref());
self
}
pub fn count(mut self, count: isize) -> Self {
self.count = Some(count);
self
}
pub fn run(self) -> HScanStream<'a> {
HScanStream::new(self.key, self.pattern, self.count, self.connection)
}
}
#[must_use]
#[allow(missing_debug_implementations)]
pub struct HScanStream<'a> {
inner: Pin<Box<ScanStream<'a>>>,
current_field: Option<Vec<u8>>,
}
impl<'a> HScanStream<'a> {
pub(crate) fn new(
key: &'a [u8],
pattern: Option<&'a [u8]>,
count: Option<isize>,
connection: &'a mut Connection,
) -> Self {
let inner = Pin::new(Box::new(ScanStream::new(
"HSCAN",
Some(key),
pattern,
count,
connection,
)));
Self {
inner,
current_field: None,
}
}
}
impl<'a> Stream for HScanStream<'a> {
type Item = (Vec<u8>, Vec<u8>);
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
match self.inner.as_mut().poll_next(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(Some(v)) => {
if self.current_field.is_some() {
let field = self.current_field.clone().unwrap();
self.current_field = None;
Poll::Ready(Some((field, v)))
} else {
self.current_field = Some(v);
cx.waker().wake_by_ref();
Poll::Pending
}
}
Poll::Ready(None) => Poll::Ready(None),
}
}
}