use async_trait::async_trait;
use fred::prelude::*;
use time::OffsetDateTime;
use crate::{
session::{SessionId, SessionRecord},
Session, SessionStore,
};
#[derive(thiserror::Error, Debug)]
pub enum RedisStoreError {
#[error("Redis error: {0}")]
Redis(#[from] fred::error::RedisError),
#[error("Rust MsgPack encode error: {0}")]
RmpSerdeEncode(#[from] rmp_serde::encode::Error),
#[error("Rust MsgPack decode error: {0}")]
RmpSerdeDecode(#[from] rmp_serde::decode::Error),
}
#[derive(Clone, Default)]
pub struct RedisStore {
client: RedisClient,
}
impl RedisStore {
pub fn new(client: RedisClient) -> Self {
Self { client }
}
}
#[async_trait]
impl SessionStore for RedisStore {
type Error = RedisStoreError;
async fn save(&self, session_record: &SessionRecord) -> Result<(), Self::Error> {
let expiration = session_record
.expiration_time()
.map(OffsetDateTime::unix_timestamp)
.map(Expiration::EXAT);
self.client
.set(
session_record.id().to_string(),
rmp_serde::to_vec(&session_record)?.as_slice(),
expiration,
None,
false,
)
.await?;
Ok(())
}
async fn load(&self, session_id: &SessionId) -> Result<Option<Session>, Self::Error> {
Ok(self
.client
.get::<Option<Vec<u8>>, _>(session_id.to_string())
.await?
.map(|bs| rmp_serde::from_slice::<SessionRecord>(&bs))
.transpose()?
.map(Into::into))
}
async fn delete(&self, session_id: &SessionId) -> Result<(), Self::Error> {
self.client.del(session_id.to_string()).await?;
Ok(())
}
}