use crate::types::SnapshotResult;
use crate::Element;
use crate::GetIterator;
use crate::IntoSkyhashAction;
use crate::IntoSkyhashBytes;
use crate::Query;
use crate::RespCode;
use crate::Response;
cfg_async!(
use core::{future::Future, pin::Pin};
);
use std::io::ErrorKind;
pub const ERR_SNAPSHOT_BUSY: &str = "err-snapshot-busy";
pub const ERR_SNAPSHOT_DISABLED: &str = "err-snapshot-disabled";
#[derive(Debug)]
pub enum ActionError {
ParseError,
UnexpectedDataType,
UnknownDataType,
InvalidResponse,
IoError(ErrorKind),
Code(RespCode),
}
cfg_async!(
pub type AsyncResult<'s, T> = Pin<Box<dyn Future<Output = T> + Send + Sync + 's>>;
#[doc(hidden)]
pub trait AsyncSocket: Send + Sync {
fn run(&mut self, q: Query) -> AsyncResult<std::io::Result<Response>>;
}
impl<T> AsyncActions for T where T: AsyncSocket {}
);
pub type ActionResult<T> = Result<T, ActionError>;
cfg_sync!(
#[doc(hidden)]
pub trait SyncSocket {
fn run(&mut self, q: Query) -> std::io::Result<Response>;
}
impl<T> Actions for T where T: SyncSocket {}
);
macro_rules! gen_match {
($ret:expr, $($($mtch:pat)+ $(if $exp:expr)*, $expect:expr),*) => {
match $ret {
$($(Ok($mtch))|* $(if $exp:expr)* => Ok($expect),)*
Ok(Response::InvalidResponse) => Err(ActionError::InvalidResponse),
Ok(Response::ParseError) => Err(ActionError::ParseError),
Ok(Response::UnsupportedDataType) => Err(ActionError::UnknownDataType),
Ok(Response::Item(Element::RespCode(code))) => Err(ActionError::Code(code)),
Ok(Response::Item(_)) => Err(ActionError::UnexpectedDataType),
Err(e) => Err(ActionError::IoError(e.kind())),
}
};
}
macro_rules! implement_actions {
(
$(
$(#[$attr:meta])+
fn $name:ident$(<$($tyargs:ident : $ty:ident $(+$tye:lifetime)*),*>)?(
$($argname:ident: $argty:ty),*) -> $ret:ty {
$($block:block)?
$($($mtch:pat)|+ => $expect:expr),+
}
)*
) => {
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
pub trait Actions: SyncSocket {
$(
$(#[$attr])*
#[inline]
fn $name<'s, $($($tyargs: $ty $(+$tye)*, )*)?>(&'s mut self $(, $argname: $argty)*) -> ActionResult<$ret> {
gen_match!(self.run($($block)?), $($($mtch)+, $expect),*)
}
)*
}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub trait AsyncActions: AsyncSocket {
$(
$(#[$attr])*
#[inline]
fn $name<'s, $($($tyargs: $ty $(+$tye)*, )*)?>(&'s mut self $(, $argname: $argty)*) -> AsyncResult<ActionResult<$ret>> {
Box::pin(async move {gen_match!(self.run($($block)?).await, $($($mtch)+, $expect),*)})
}
)*
}
};
}
implement_actions!(
fn dbsize() -> usize {
{ Query::from("dbsize") }
Response::Item(Element::UnsignedInt(int)) => int as usize
}
fn del(key: impl IntoSkyhashAction + 's) -> usize {
{ Query::from("del").arg(key) }
Response::Item(Element::UnsignedInt(int)) => int as usize
}
fn exists(key: impl IntoSkyhashAction + 's) -> usize {
{ Query::from("exists").arg(key) }
Response::Item(Element::UnsignedInt(int)) => int as usize
}
fn flushdb() -> () {
{ Query::from("flushdb") }
Response::Item(Element::RespCode(RespCode::Okay)) => {}
}
fn get(key: impl IntoSkyhashBytes + 's) -> String {
{ Query::from("get").arg(key)}
Response::Item(Element::String(st)) => st
}
fn keylen(key: impl IntoSkyhashBytes + 's) -> usize {
{ Query::from("keylen").arg(key)}
Response::Item(Element::UnsignedInt(int)) => int as usize
}
fn lskeys(count: usize) -> Vec<String> {
{ Query::from("lskeys").arg(count)}
Response::Item(Element::FlatArray(arr)) => arr
}
fn mget(keys: impl IntoSkyhashAction+ 's) -> Vec<Element> {
{ Query::from("mget").arg(keys)}
Response::Item(Element::Array(array)) => array
}
fn mksnap() -> SnapshotResult {
{ Query::from("mksnap")}
Response::Item(Element::RespCode(RespCode::Okay)) => SnapshotResult::Okay,
Response::Item(Element::RespCode(RespCode::ErrorString(er))) => {
match er.as_str() {
ERR_SNAPSHOT_BUSY => SnapshotResult::Busy,
ERR_SNAPSHOT_DISABLED => SnapshotResult::Disabled,
_ => return Err(ActionError::InvalidResponse)
}
}
}
fn mset<T: IntoSkyhashBytes + 's , U: IntoSkyhashBytes + 's>
(
keys: impl GetIterator<T> + 's,
values: impl GetIterator<U> + 's
) -> usize {
{
assert!(keys.incr_len_by() == values.incr_len_by(), "The number of keys and values for mset must be equal");
Query::from("mset")._push_alt_iter(keys, values)
}
Response::Item(Element::UnsignedInt(int)) => int as usize
}
fn mupdate<T: IntoSkyhashBytes + 's , U: IntoSkyhashBytes + 's>
(
keys: impl GetIterator<T> + 's,
values: impl GetIterator<U> + 's
) -> usize {
{
assert!(keys.incr_len_by() == values.incr_len_by(), "The number of keys and values for mupdate must be equal");
Query::from("mset")._push_alt_iter(keys, values)
}
Response::Item(Element::UnsignedInt(int)) => int as usize
}
fn pop(keys: impl IntoSkyhashAction + 's) -> Vec<Element> {
{ Query::from("POP").arg(keys) }
Response::Item(Element::Array(arr)) => arr
}
fn sdel(keys: impl IntoSkyhashAction + 's) -> bool {
{ Query::from("sdel").arg(keys) }
Response::Item(Element::RespCode(RespCode::Okay)) => true,
Response::Item(Element::RespCode(RespCode::NotFound)) => false
}
fn set(key: impl IntoSkyhashBytes + 's, value: impl IntoSkyhashBytes + 's) -> () {
{ Query::from("set").arg(key).arg(value) }
Response::Item(Element::RespCode(RespCode::Okay)) => {}
}
fn sset<T: IntoSkyhashBytes + 's , U: IntoSkyhashBytes + 's>
(
keys: impl GetIterator<T> + 's,
values: impl GetIterator<U> + 's
) -> bool {
{
assert!(
keys.incr_len_by() == values.incr_len_by(),
"The number of keys and values for sset must be equal"
);
Query::from("sset")._push_alt_iter(keys, values)
}
Response::Item(Element::RespCode(RespCode::Okay)) => true,
Response::Item(Element::RespCode(RespCode::OverwriteError)) => false
}
fn supdate<T: IntoSkyhashBytes + 's , U: IntoSkyhashBytes + 's>
(
keys: impl GetIterator<T> + 's,
values: impl GetIterator<U> + 's
) -> bool {
{
assert!(
keys.incr_len_by() == values.incr_len_by(),
"The number of keys and values for supdate must be equal"
);
Query::from("supdate")._push_alt_iter(keys, values)
}
Response::Item(Element::RespCode(RespCode::Okay)) => true,
Response::Item(Element::RespCode(RespCode::NotFound)) => false
}
fn update(key: impl IntoSkyhashBytes + 's, value: impl IntoSkyhashBytes + 's) -> () {
{ Query::from("update").arg(key).arg(value) }
Response::Item(Element::RespCode(RespCode::Okay)) => {}
}
fn uset<T: IntoSkyhashBytes + 's , U: IntoSkyhashBytes + 's>
(
keys: impl GetIterator<T> + 's,
values: impl GetIterator<U> + 's
) -> usize {
{
assert!(
keys.incr_len_by() == values.incr_len_by(),
"The number of keys and values for uset must be equal"
);
Query::from("uset")._push_alt_iter(keys, values)
}
Response::Item(Element::UnsignedInt(int)) => int as usize
}
);