use crate::{KevyError, KevyResult};
use std::io::{Read, Write};
use std::net::TcpStream;
use kevy_embedded::PubsubFrame;
use kevy_resp::{Reply, encode_command};
use kevy_resp_client::{ReplyReadBuf, classify_pubsub};
use crate::subscribe::PubsubEvent;
pub(crate) fn send_to(
stream: &mut TcpStream,
verb: &[u8],
args: &[&[u8]],
) -> KevyResult<()> {
let mut argv = Vec::with_capacity(args.len() + 1);
argv.push(verb.to_vec());
argv.extend(args.iter().map(|a| a.to_vec()));
let mut frame = Vec::new();
encode_command(&mut frame, &argv);
Ok(stream.write_all(&frame)?)
}
pub(crate) fn await_acks(
stream: &mut std::net::TcpStream,
buf: &mut ReplyReadBuf,
pending: &mut std::collections::VecDeque<PubsubEvent>,
n: usize,
want_pattern_ack: bool,
) -> KevyResult<()> {
let mut seen = 0usize;
while seen < n {
let ev = recv_remote(stream, buf)?;
let is_ack = if want_pattern_ack {
matches!(ev, PubsubEvent::Psubscribe { .. })
} else {
matches!(ev, PubsubEvent::Subscribe { .. })
};
if is_ack {
seen += 1;
}
pending.push_back(ev);
}
Ok(())
}
pub(crate) fn recv_remote(
stream: &mut TcpStream,
buf: &mut ReplyReadBuf,
) -> KevyResult<PubsubEvent> {
let mut chunk = [0u8; 8192];
loop {
match buf.parse_next() {
Ok(Some(reply)) => {
return classify_pubsub(reply).map_err(|e| KevyError::Protocol(e.to_string()));
}
Ok(None) => {}
Err(_) => {
return Err(KevyError::Protocol("malformed reply".into()));
}
}
let n = stream.read(&mut chunk)?;
if n == 0 {
return Err(KevyError::Closed);
}
buf.extend(&chunk[..n]);
}
}
pub(crate) fn frame_to_event(frame: PubsubFrame) -> PubsubEvent {
match frame {
PubsubFrame::Subscribe { channel, count } => PubsubEvent::Subscribe {
channel,
count: count as i64,
},
PubsubFrame::Psubscribe { pattern, count } => PubsubEvent::Psubscribe {
pattern,
count: count as i64,
},
PubsubFrame::Unsubscribe { channel, count } => PubsubEvent::Unsubscribe {
channel,
count: count as i64,
},
PubsubFrame::Punsubscribe { pattern, count } => PubsubEvent::Punsubscribe {
pattern,
count: count as i64,
},
PubsubFrame::Message { channel, payload } => PubsubEvent::Message { channel, payload },
PubsubFrame::Pmessage {
pattern,
channel,
payload,
} => PubsubEvent::Pmessage {
pattern,
channel,
payload,
},
}
}
pub(crate) fn shape(r: &Reply) -> &'static str {
match r {
Reply::Simple(_) => "simple-string",
Reply::Error(_) => "error",
Reply::Int(_) => "integer",
Reply::Bulk(_) => "bulk-string",
Reply::Nil | Reply::Null => "nil",
Reply::Array(_) => "array",
Reply::Map(_) => "map",
Reply::Set(_) => "set",
Reply::Double(_) => "double",
Reply::Boolean(_) => "boolean",
Reply::Verbatim { .. } => "verbatim-string",
Reply::BigNumber(_) => "big-number",
Reply::Push(_) => "push",
Reply::BlobError(_) => "blob-error",
}
}
pub(crate) fn invalid(msg: impl Into<String>) -> KevyError {
KevyError::Protocol(msg.into())
}