#![forbid(unsafe_code)]
#![warn(missing_docs)]
use std::time::Duration;
use kevy_embedded::Store;
use kevy_resp_client::RespClient;
mod blocking;
mod cluster;
mod cluster_coll;
mod collections;
mod feed;
mod hash_ttl;
mod index;
mod pipeline;
mod reply;
mod scan;
mod subscribe;
mod subscribe_io;
mod transaction;
mod url;
mod zalgebra;
pub use blocking::ZPopHit;
pub use cluster::ClusterClient;
pub use feed::{FeedBatch, FeedFrame};
pub use index::{IdxInfo, IdxPage, IdxRow, IdxType};
pub use pipeline::PipelineBuf;
pub use subscribe::{PubsubEvent, Subscriber, SubscriberEvents, SubscriberMessages};
pub use transaction::{Transaction, TransactionReplies};
pub use kevy_embedded::{HExpireCode, HExpireCond, KevyError, KevyResult, StoreError, ZAggregate};
pub use kevy_resp::Reply;
pub(crate) use reply::{array_to_bulks, num_f64, num_u64, store_err, string, unexpected, vec2, vec3};
pub(crate) use url::{Target, parse_url, resolve_store};
pub enum Connection {
Embedded(Box<Store>),
Remote(RespClient),
}
impl Connection {
pub fn connect(url: &str) -> KevyResult<Self> {
let parsed = parse_url(url)?;
match parsed {
Target::Remote(remote_url) => Ok(Self::Remote(RespClient::connect_url(&remote_url)?)),
embed => Ok(Self::Embedded(Box::new(resolve_store(&embed)?))),
}
}
pub(crate) fn remote(&mut self, feature: &str) -> KevyResult<&mut RespClient> {
match self {
Self::Embedded(_) => Err(KevyError::Unsupported(format!(
"{feature} is remote-only; on the embedded backend match \
Connection::Embedded and use kevy_embedded::Store's typed API"
))),
Self::Remote(c) => Ok(c),
}
}
pub fn ping(&mut self) -> KevyResult<()> {
match self {
Self::Embedded(_) => Ok(()),
Self::Remote(c) => match c.request_borrowed(&[b"PING"])? {
Reply::Simple(s) if s == b"PONG" => Ok(()),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
},
}
}
pub fn set(&mut self, key: &[u8], value: &[u8]) -> KevyResult<()> {
match self {
Self::Embedded(s) => s.set(key, value).map(|_| ()),
Self::Remote(c) => match c.request_borrowed(&[b"SET", key, value])? {
Reply::Simple(s) if s == b"OK" => Ok(()),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
},
}
}
pub fn get(&mut self, key: &[u8]) -> KevyResult<Option<Vec<u8>>> {
match self {
Self::Embedded(s) => s.get(key),
Self::Remote(c) => match c.request_borrowed(&[b"GET", key])? {
Reply::Bulk(v) => Ok(Some(v)),
Reply::Nil => Ok(None),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
},
}
}
pub fn del(&mut self, keys: &[&[u8]]) -> KevyResult<usize> {
match self {
Self::Embedded(s) => s.del(keys),
Self::Remote(c) => {
let mut args: Vec<&[u8]> = Vec::with_capacity(keys.len() + 1);
args.push(b"DEL");
args.extend_from_slice(keys);
match c.request_borrowed(&args)? {
Reply::Int(n) if n >= 0 => Ok(n as usize),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
}
}
}
}
pub fn exists(&mut self, keys: &[&[u8]]) -> KevyResult<usize> {
match self {
Self::Embedded(s) => s.exists(keys),
Self::Remote(c) => {
let mut args: Vec<&[u8]> = Vec::with_capacity(keys.len() + 1);
args.push(b"EXISTS");
args.extend_from_slice(keys);
match c.request_borrowed(&args)? {
Reply::Int(n) if n >= 0 => Ok(n as usize),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
}
}
}
}
pub fn incr(&mut self, key: &[u8]) -> KevyResult<i64> {
match self {
Self::Embedded(s) => s.incr(key),
Self::Remote(c) => match c.request_borrowed(&[b"INCR", key])? {
Reply::Int(n) => Ok(n),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
},
}
}
pub fn incr_by(&mut self, key: &[u8], delta: i64) -> KevyResult<i64> {
match self {
Self::Embedded(s) => s.incr_by(key, delta),
Self::Remote(c) => {
let delta_s = delta.to_string();
match c.request_borrowed(&[b"INCRBY", key, delta_s.as_bytes()])? {
Reply::Int(n) => Ok(n),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
}
}
}
}
pub fn expire(&mut self, key: &[u8], ttl: Duration) -> KevyResult<bool> {
match self {
Self::Embedded(s) => s.expire(key, ttl),
Self::Remote(c) => {
let ms = ttl.as_millis().min(i64::MAX as u128) as i64;
let ms_s = ms.to_string();
match c.request_borrowed(&[b"PEXPIRE", key, ms_s.as_bytes()])? {
Reply::Int(1) => Ok(true),
Reply::Int(0) => Ok(false),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
}
}
}
}
pub fn persist(&mut self, key: &[u8]) -> KevyResult<bool> {
match self {
Self::Embedded(s) => s.persist(key),
Self::Remote(c) => match c.request_borrowed(&[b"PERSIST", key])? {
Reply::Int(1) => Ok(true),
Reply::Int(0) => Ok(false),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
},
}
}
pub fn ttl_ms(&mut self, key: &[u8]) -> KevyResult<i64> {
match self {
Self::Embedded(s) => Ok(s.ttl_ms(key)),
Self::Remote(c) => match c.request_borrowed(&[b"PTTL", key])? {
Reply::Int(n) => Ok(n),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
},
}
}
pub fn type_of(&mut self, key: &[u8]) -> KevyResult<String> {
match self {
Self::Embedded(s) => Ok(s.type_of(key).to_string()),
Self::Remote(c) => match c.request_borrowed(&[b"TYPE", key])? {
Reply::Simple(s) => Ok(string(s)),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
},
}
}
pub fn dbsize(&mut self) -> KevyResult<usize> {
match self {
Self::Embedded(s) => Ok(s.dbsize()),
Self::Remote(c) => match c.request_borrowed(&[b"DBSIZE"])? {
Reply::Int(n) if n >= 0 => Ok(n as usize),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
},
}
}
pub fn flushall(&mut self) -> KevyResult<()> {
match self {
Self::Embedded(s) => s.flushall(),
Self::Remote(c) => match c.request_borrowed(&[b"FLUSHALL"])? {
Reply::Simple(s) if s == b"OK" => Ok(()),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
},
}
}
pub fn set_with_ttl(&mut self, key: &[u8], value: &[u8], ttl: Duration) -> KevyResult<()> {
match self {
Self::Embedded(s) => s.set_with_ttl(key, value, ttl).map(|_| ()),
Self::Remote(c) => {
let ms = ttl.as_millis().min(i64::MAX as u128) as i64;
let ms_s = ms.to_string();
match c.request_borrowed(&[b"SET", key, value, b"PX", ms_s.as_bytes()])? {
Reply::Simple(s) if s == b"OK" => Ok(()),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
}
}
}
}
pub fn mget(&mut self, keys: &[&[u8]]) -> KevyResult<Vec<Option<Vec<u8>>>> {
match self {
Self::Embedded(s) => keys.iter().map(|k| s.get(k)).collect(),
Self::Remote(c) => {
let mut args: Vec<&[u8]> = Vec::with_capacity(keys.len() + 1);
args.push(b"MGET");
args.extend_from_slice(keys);
match c.request_borrowed(&args)? {
Reply::Array(items) => items
.into_iter()
.map(|r| match r {
Reply::Bulk(v) => Ok(Some(v)),
Reply::Nil => Ok(None),
other => Err(unexpected(other)),
})
.collect(),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
}
}
}
}
pub fn mset(&mut self, pairs: &[(&[u8], &[u8])]) -> KevyResult<()> {
match self {
Self::Embedded(s) => {
for (k, v) in pairs {
s.set(k, v)?;
}
Ok(())
}
Self::Remote(c) => {
let mut args: Vec<&[u8]> = Vec::with_capacity(pairs.len() * 2 + 1);
args.push(b"MSET");
for &(k, v) in pairs {
args.push(k);
args.push(v);
}
match c.request_borrowed(&args)? {
Reply::Simple(s) if s == b"OK" => Ok(()),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
}
}
}
}
pub fn publish(&mut self, channel: &[u8], message: &[u8]) -> KevyResult<usize> {
match self {
Self::Embedded(s) => Ok(s.publish(channel, message)),
Self::Remote(c) => match c.request_borrowed(&[b"PUBLISH", channel, message])? {
Reply::Int(n) if n >= 0 => Ok(n as usize),
Reply::Error(e) => Err(KevyError::Protocol(string(e))),
other => Err(unexpected(other)),
},
}
}
}
#[cfg(test)]
#[path = "lib_tests.rs"]
mod tests;