use std::{
fmt::Debug,
marker::{Send, Sync},
};
use std::time::Duration;
use anyhow::{anyhow, Result};
use redis::{AsyncCommands, Client, ConnectionLike, ToRedisArgs};
use serde::{de::DeserializeOwned, Serialize};
use tracing::*;
use crate::config::DiscordConfig;
pub type KVIdentity = String;
#[derive(Debug)]
pub struct KVClient {
client: Client,
}
pub trait Identifiable {
fn kv_key(&self) -> KVIdentity;
}
impl KVClient {
#[instrument(level = "trace")]
pub fn new(config: &DiscordConfig) -> Result<Self> {
let client = redis::Client::open(config.redis_url.as_ref())?;
Ok(Self { client })
}
pub async fn save<T>(&self, record: T) -> Result<()>
where
T: Identifiable + Serialize + Send + Sync + ToRedisArgs + Debug,
{
let span = trace_span!(
"storage.kv_client",
record = field::Empty,
save_key = field::Empty
);
let _enter = span.enter();
span.record("record", field::debug(&record));
event!(
parent: &span,
Level::TRACE,
"Saving a record to the KV store"
);
let con = &mut self.client.get_multiplexed_async_connection().await?;
let save_key = &record.kv_key();
span.record("save_key", field::debug(&save_key));
match con.set(save_key, record).await {
Ok(()) => Ok(()),
Err(e) => {
error!(?e, "Error saving valing to kv store");
Err(anyhow!(e))
}
}
}
pub async fn save_json<T>(&self, record: T) -> Result<()>
where
T: Identifiable + Serialize + Send + Sync + Debug,
{
let span = trace_span!(
"storage.kv_client",
record = field::Empty,
save_key = field::Empty
);
let _enter = span.enter();
span.record("record", field::debug(&record));
event!(Level::TRACE, "Saving a record to the KV store in json mode");
let json = serde_json::to_string(&record)?;
let con = &mut self.client.get_multiplexed_async_connection().await?;
let save_key = &record.kv_key();
span.record("save_key", field::debug(&save_key));
match con.set(save_key, json).await {
Ok(()) => Ok(()),
Err(e) => {
error!(?e, "Error saving valing to kv store");
Err(anyhow!(e))
}
}
}
pub async fn get_json<T>(&self, record: &T) -> Result<Option<T>>
where
T: DeserializeOwned + Send + Sync + Debug + Identifiable,
{
let span = trace_span!(
"storage.kv_client",
record = field::Empty,
object_key = field::Empty
);
let _enter = span.enter();
let key = record.kv_key();
span.record("object_key", &key);
event!(Level::TRACE, "Retrieving a record from the kv store");
let con = &mut self.client.get_multiplexed_async_connection().await?;
if !con.exists(&key).await? {
return Ok(None);
}
let json: String = con.get(key).await?;
let record = serde_json::from_str(json.as_str())?;
span.record("record", field::debug(&record));
Ok(Some(record))
}
pub async fn connection_check(&self) -> Result<()> {
let connection_result = self.client.get_connection_with_timeout(Duration::from_secs(15));
match connection_result {
Ok(conn) => {
if conn.is_open() {
Ok(())
} else {
Err(anyhow!("Could not open connection"))
}
},
Err(e) => Err(anyhow!(e))
}
}
}