      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104

    use std::collections::HashMap;

    use derive_more::Display;
    use time::Duration;

    use super::SessionKey;

    pub(crate) type SessionState = HashMap<String, String>;

    /// The interface to retrieve and save the current session data from/to the chosen storage backend.
    ///
    /// You can provide your own custom session store backend by implementing this trait.
    #[async_trait::async_trait(?Send)]
    pub trait SessionStore {
        /// Loads the session state associated to a session key.
        async fn load(&self, session_key: &SessionKey) -> Result<Option<SessionState>, LoadError>;

        /// Persist the session state for a newly created session.
        ///
        /// Returns the corresponding session key.
        async fn save(
            &self,
            session_state: SessionState,
            ttl: &Duration,
        ) -> Result<SessionKey, SaveError>;

        /// Updates the session state associated to a pre-existing session key.
        async fn update(
            &self,
            session_key: SessionKey,
            session_state: SessionState,
            ttl: &Duration,
        ) -> Result<SessionKey, UpdateError>;

        /// Deletes a session from the store.
        async fn delete(&self, session_key: &SessionKey) -> Result<(), anyhow::Error>;
    }

    // We cannot derive the `Error` implementation using `derive_more` for our custom errors:
    // `derive_more`'s `#[error(source)]` attribute requires the source implement the `Error` trait,
    // while it's actually enough for it to be able to produce a reference to a dyn Error.

    /// Possible failures modes for [`SessionStore::load`].
    #[derive(Debug, Display)]
    pub enum LoadError {
        /// Failed to deserialize session state.
        #[display(fmt = "Failed to deserialize session state")]
        Deserialization(anyhow::Error),

        /// Something went wrong when retrieving the session state.
        #[display(fmt = "Something went wrong when retrieving the session state")]
        Other(anyhow::Error),
    }

    impl std::error::Error for LoadError {
        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
            match self {
                Self::Deserialization(err) => Some(err.as_ref()),
                Self::Other(err) => Some(err.as_ref()),
            }
        }
    }

    /// Possible failures modes for [`SessionStore::save`].
    #[derive(Debug, Display)]
    pub enum SaveError {
        /// Failed to serialize session state.
        #[display(fmt = "Failed to serialize session state")]
        Serialization(anyhow::Error),

        /// Something went wrong when persisting the session state.
        #[display(fmt = "Something went wrong when persisting the session state")]
        Other(anyhow::Error),
    }

    impl std::error::Error for SaveError {
        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
            match self {
                Self::Serialization(err) => Some(err.as_ref()),
                Self::Other(err) => Some(err.as_ref()),
            }
        }
    }

    #[derive(Debug, Display)]
    /// Possible failures modes for [`SessionStore::update`].
    pub enum UpdateError {
        /// Failed to serialize session state.
        #[display(fmt = "Failed to serialize session state")]
        Serialization(anyhow::Error),

        /// Something went wrong when updating the session state.
        #[display(fmt = "Something went wrong when updating the session state.")]
        Other(anyhow::Error),
    }

    impl std::error::Error for UpdateError {
        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
            match self {
                Self::Serialization(err) => Some(err.as_ref()),
                Self::Other(err) => Some(err.as_ref()),
            }
        }
    }