tower_sessions_redis_store/
lib.rs1use std::fmt::Debug;
2
3use async_trait::async_trait;
4pub use fred;
5use fred::{
6 prelude::KeysInterface,
7 types::{Expiration, SetOptions},
8};
9use time::OffsetDateTime;
10use tower_sessions_core::{
11 session::{Id, Record},
12 session_store, SessionStore,
13};
14
15#[derive(Debug, thiserror::Error)]
16pub enum RedisStoreError {
17 #[error(transparent)]
18 Redis(#[from] fred::error::Error),
19
20 #[error(transparent)]
21 Decode(#[from] rmp_serde::decode::Error),
22
23 #[error(transparent)]
24 Encode(#[from] rmp_serde::encode::Error),
25}
26
27impl From<RedisStoreError> for session_store::Error {
28 fn from(err: RedisStoreError) -> Self {
29 match err {
30 RedisStoreError::Redis(inner) => session_store::Error::Backend(inner.to_string()),
31 RedisStoreError::Decode(inner) => session_store::Error::Decode(inner.to_string()),
32 RedisStoreError::Encode(inner) => session_store::Error::Encode(inner.to_string()),
33 }
34 }
35}
36
37#[derive(Debug, Clone, Default)]
39pub struct RedisStore<C: KeysInterface + Send + Sync> {
40 client: C,
41}
42
43impl<C: KeysInterface + Send + Sync> RedisStore<C> {
44 pub fn new(client: C) -> Self {
61 Self { client }
62 }
63
64 async fn save_with_options(
65 &self,
66 record: &Record,
67 options: Option<SetOptions>,
68 ) -> session_store::Result<bool> {
69 let expire = Some(Expiration::EXAT(OffsetDateTime::unix_timestamp(
70 record.expiry_date,
71 )));
72
73 Ok(self
74 .client
75 .set(
76 record.id.to_string(),
77 rmp_serde::to_vec(&record)
78 .map_err(RedisStoreError::Encode)?
79 .as_slice(),
80 expire,
81 options,
82 false,
83 )
84 .await
85 .map_err(RedisStoreError::Redis)?)
86 }
87}
88
89#[async_trait]
90impl<C> SessionStore for RedisStore<C>
91where
92 C: KeysInterface + Send + Sync + Debug + 'static,
93{
94 async fn create(&self, record: &mut Record) -> session_store::Result<()> {
95 loop {
96 if !self.save_with_options(record, Some(SetOptions::NX)).await? {
97 record.id = Id::default();
98 continue;
99 }
100 break;
101 }
102 Ok(())
103 }
104
105 async fn save(&self, record: &Record) -> session_store::Result<()> {
106 self.save_with_options(record, Some(SetOptions::XX)).await?;
107 Ok(())
108 }
109
110 async fn load(&self, session_id: &Id) -> session_store::Result<Option<Record>> {
111 let data = self
112 .client
113 .get::<Option<Vec<u8>>, _>(session_id.to_string())
114 .await
115 .map_err(RedisStoreError::Redis)?;
116
117 if let Some(data) = data {
118 Ok(Some(
119 rmp_serde::from_slice(&data).map_err(RedisStoreError::Decode)?,
120 ))
121 } else {
122 Ok(None)
123 }
124 }
125
126 async fn delete(&self, session_id: &Id) -> session_store::Result<()> {
127 let _: () = self
128 .client
129 .del(session_id.to_string())
130 .await
131 .map_err(RedisStoreError::Redis)?;
132 Ok(())
133 }
134}