#![doc = include_str!("../README.md")]
mod errors;
pub mod states;
pub use errors::{SourceError, SourceResult};
use std::{collections::HashMap, fmt::Debug, marker::PhantomData};
use config::{AsyncSource, ConfigError, Map};
use redis::AsyncCommands;
#[cfg(feature = "json")]
use redis::JsonAsyncCommands;
#[derive(Debug)]
pub struct RedisSource<SK, S = states::PlainString> {
client: redis::Client,
source_key: SK,
required: bool,
state: PhantomData<S>,
}
impl<SK, S> RedisSource<SK, S> {
pub fn try_new<I: redis::IntoConnectionInfo>(
source_key: SK,
connection_info: I,
) -> SourceResult<Self> {
let client = redis::Client::open(connection_info)?;
Ok(Self {
client,
source_key,
required: true,
state: PhantomData,
})
}
#[must_use]
pub fn required(mut self, required: bool) -> Self {
self.required = required;
self
}
}
impl<SK: redis::ToRedisArgs + Clone + Send + Sync> RedisSource<SK, states::PlainString> {
async fn collect_from_key(&self) -> SourceResult<Map<String, config::Value>> {
let data: Option<Vec<u8>> = self
.client
.get_multiplexed_async_connection()
.await?
.get(self.source_key.clone())
.await?;
if !self.required && data.is_none() {
return Ok(Map::new());
}
let data = data.ok_or(SourceError::RedisKeyDoesNotExist)?;
Ok(serde_json::from_slice(&data)?)
}
}
impl<SK: redis::ToRedisArgs + Clone + Send + Sync> RedisSource<SK, states::Hash> {
async fn collect_from_hash(&self) -> SourceResult<Map<String, config::Value>> {
let data: Option<HashMap<String, Vec<u8>>> = self
.client
.get_multiplexed_async_connection()
.await?
.hgetall(self.source_key.clone())
.await?;
if !self.required && data.is_none() {
return Ok(Map::new());
}
let data = data.ok_or(SourceError::RedisKeyDoesNotExist)?;
data.into_iter()
.map(|(k, v)| -> Result<(String, config::Value), SourceError> {
Ok((k, serde_json::from_slice(&v)?))
})
.collect::<Result<HashMap<String, config::Value>, _>>()
}
}
#[cfg(feature = "json")]
impl<SK: redis::ToRedisArgs + Clone + Send + Sync> RedisSource<SK, states::Json> {
async fn collect_from_json(&self) -> SourceResult<Map<String, config::Value>> {
let data: Option<Vec<u8>> = self
.client
.get_multiplexed_async_connection()
.await?
.json_get(self.source_key.clone(), "$")
.await?;
if !self.required && data.is_none() {
return Ok(Map::new());
}
let data = data.ok_or(SourceError::RedisKeyDoesNotExist)?;
let data: Vec<Map<String, config::Value>> =
serde_json::from_slice(&data).map_err(SourceError::SerdeError)?;
data.into_iter()
.next()
.ok_or(SourceError::RedisKeyDoesNotExist)
}
}
#[async_trait::async_trait]
impl<SK: redis::ToRedisArgs + Clone + Send + Sync + Debug> AsyncSource
for RedisSource<SK, states::Hash>
{
async fn collect(&self) -> Result<Map<String, config::Value>, ConfigError> {
self.collect_from_hash()
.await
.map_err(|err| ConfigError::NotFound(err.to_string()))
}
}
#[cfg(feature = "json")]
#[async_trait::async_trait]
impl<SK: redis::ToRedisArgs + Clone + Send + Sync + Debug> AsyncSource
for RedisSource<SK, states::Json>
{
async fn collect(&self) -> Result<Map<String, config::Value>, ConfigError> {
self.collect_from_json()
.await
.map_err(|err| ConfigError::NotFound(err.to_string()))
}
}
#[async_trait::async_trait]
impl<SK: redis::ToRedisArgs + Clone + Send + Sync + Debug> AsyncSource
for RedisSource<SK, states::PlainString>
{
async fn collect(&self) -> Result<Map<String, config::Value>, ConfigError> {
self.collect_from_key()
.await
.map_err(|err| ConfigError::NotFound(err.to_string()))
}
}