#[cfg(feature = "i-server")]
use crate::types::ShutdownFlags;
use crate::{
clients::WithOptions,
commands,
error::Error,
interfaces::{FredResult, Resp3Frame},
modules::inner::ClientInner,
prelude::default_send_command,
protocol::command::Command,
router::commands as router_commands,
runtime::{BroadcastReceiver, JoinHandle, RefCount, spawn},
types::{
ClientState,
ConnectHandle,
CustomCommand,
FromValue,
InfoKind,
Value,
config::{Config, ConnectionConfig, Options, PerformanceConfig, ReconnectPolicy, Server},
},
utils,
};
use redis_protocol::resp3::types::RespVersion;
use semver::Version;
use std::future::Future;
#[cfg(any(feature = "dns", feature = "trust-dns-resolver"))]
use crate::protocol::types::Resolve;
pub trait ClientLike: Clone + Sized {
#[doc(hidden)]
fn inner(&self) -> &RefCount<ClientInner>;
#[doc(hidden)]
fn change_command(&self, _: &mut Command) {}
#[doc(hidden)]
fn send_command<C>(&self, command: C) -> Result<(), Error>
where
C: Into<Command>,
{
let mut command: Command = command.into();
self.change_command(&mut command);
default_send_command(self.inner(), command)
}
fn id(&self) -> &str {
&self.inner().id
}
fn client_config(&self) -> Config {
self.inner().config.as_ref().clone()
}
fn client_reconnect_policy(&self) -> Option<ReconnectPolicy> {
self.inner().policy.read().clone()
}
fn connection_config(&self) -> &ConnectionConfig {
self.inner().connection.as_ref()
}
fn protocol_version(&self) -> RespVersion {
if self.inner().is_resp3() {
RespVersion::RESP3
} else {
RespVersion::RESP2
}
}
fn has_reconnect_policy(&self) -> bool {
self.inner().policy.read().is_some()
}
fn is_clustered(&self) -> bool {
self.inner().config.server.is_clustered()
}
fn uses_sentinels(&self) -> bool {
self.inner().config.server.is_sentinel()
}
fn update_perf_config(&self, config: PerformanceConfig) {
self.inner().update_performance_config(config);
}
fn perf_config(&self) -> PerformanceConfig {
self.inner().performance_config()
}
fn state(&self) -> ClientState {
self.inner().state.read().clone()
}
fn is_connected(&self) -> bool {
*self.inner().state.read() == ClientState::Connected
}
fn active_connections(&self) -> Vec<Server> {
self.inner().active_connections()
}
fn server_version(&self) -> Option<Version> {
self.inner().server_state.read().kind.server_version()
}
#[cfg(feature = "dns")]
#[cfg_attr(docsrs, doc(cfg(feature = "dns")))]
fn set_resolver(&self, resolver: RefCount<dyn Resolve>) -> impl Future {
async move { self.inner().set_resolver(resolver).await }
}
fn connect(&self) -> ConnectHandle {
let inner = self.inner().clone();
utils::reset_router_task(&inner);
spawn(async move {
inner.backchannel.clear_router_state(&inner).await;
let result = router_commands::start(&inner).await;
_trace!(inner, "Ending connection task with {:?}", result);
if let Err(ref error) = result {
if !error.is_canceled() {
inner.notifications.broadcast_connect(Err(error.clone()));
}
}
inner.cas_client_state(ClientState::Disconnecting, ClientState::Disconnected);
result
})
}
fn force_reconnection(&self) -> impl Future<Output = FredResult<()>> {
async move { commands::server::force_reconnection(self.inner()).await }
}
fn wait_for_connect(&self) -> impl Future<Output = FredResult<()>> {
async move {
if utils::read_locked(&self.inner().state) == ClientState::Connected {
debug!("{}: Client is already connected.", self.inner().id);
Ok(())
} else {
self.inner().notifications.connect.load().subscribe().recv().await?
}
}
}
fn init(&self) -> impl Future<Output = FredResult<ConnectHandle>> {
async move {
let mut rx = { self.inner().notifications.connect.load().subscribe() };
let task = self.connect();
let error = rx.recv().await.map_err(Error::from).and_then(|r| r).err();
if let Some(error) = error {
utils::reset_router_task(self.inner());
Err(error)
} else {
Ok(task)
}
}
}
fn quit(&self) -> impl Future<Output = FredResult<()>> {
async move { commands::server::quit(self).await }
}
#[cfg(feature = "i-server")]
#[cfg_attr(docsrs, doc(cfg(feature = "i-server")))]
fn shutdown(&self, flags: Option<ShutdownFlags>) -> impl Future<Output = FredResult<()>> {
async move { commands::server::shutdown(self, flags).await }
}
fn flushall<R>(&self, r#async: bool) -> impl Future<Output = FredResult<R>>
where
R: FromValue,
{
async move { commands::server::flushall(self, r#async).await?.convert() }
}
fn flushall_cluster(&self) -> impl Future<Output = FredResult<()>> {
async move { commands::server::flushall_cluster(self).await }
}
fn ping<R>(&self, message: Option<String>) -> impl Future<Output = FredResult<R>>
where
R: FromValue,
{
async move { commands::server::ping(self, message).await?.convert() }
}
fn info<R>(&self, section: Option<InfoKind>) -> impl Future<Output = FredResult<R>>
where
R: FromValue,
{
async move { commands::server::info(self, section).await?.convert() }
}
fn custom<R, T>(&self, cmd: CustomCommand, args: Vec<T>) -> impl Future<Output = FredResult<R>>
where
R: FromValue,
T: TryInto<Value>,
T::Error: Into<Error>,
{
async move {
let args = utils::try_into_vec(args)?;
commands::server::custom(self, cmd, args).await?.convert()
}
}
fn custom_raw<T>(&self, cmd: CustomCommand, args: Vec<T>) -> impl Future<Output = FredResult<Resp3Frame>>
where
T: TryInto<Value>,
T::Error: Into<Error>,
{
async move {
let args = utils::try_into_vec(args)?;
commands::server::custom_raw(self, cmd, args).await
}
}
fn with_options(&self, options: &Options) -> WithOptions<Self> {
WithOptions {
client: self.clone(),
options: options.clone(),
}
}
}
pub fn spawn_event_listener<T, F, Fut>(mut rx: BroadcastReceiver<T>, func: F) -> JoinHandle<FredResult<()>>
where
T: Clone + 'static,
Fut: Future<Output = FredResult<()>> + 'static,
F: Fn(T) -> Fut + 'static,
{
spawn(async move {
let mut result = Ok(());
while let Ok(val) = rx.recv().await {
if let Err(err) = func(val).await {
result = Err(err);
break;
}
}
result
})
}