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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::{
    fmt,
    sync::{
        atomic::{AtomicUsize, Ordering},
        Arc, RwLock, RwLockReadGuard, RwLockWriteGuard,
    },
    time::Duration,
};

use crate::{
    anyhow,
    data::{from_value, to_value, DeserializeOwned, Serialize},
    Config, Data, Result, Storage,
};

/// Session
pub struct Session {
    /// Session's id
    pub id: String,
    /// Session's status
    pub status: AtomicUsize,
    data: Arc<RwLock<Data>>,
    config: Arc<Config>,
}

impl Session {
    /// Creates new `Session` with `id` `status` and `Config`
    pub fn new(id: &str, status: usize, config: Arc<Config>) -> Self {
        Self {
            id: id.to_string(),
            status: status.into(),
            data: Default::default(),
            config,
        }
    }

    /// Reads the session expires or cookie max_age
    pub fn max_age(&self) -> Duration {
        self.config.max_age()
    }

    /// Reads the session state
    pub fn data(&self) -> Result<RwLockReadGuard<'_, Data>> {
        self.data.read().map_err(|e| anyhow!(e.to_string()))
    }

    /// Writes the session state
    pub fn data_mut(&self) -> Result<RwLockWriteGuard<'_, Data>> {
        self.data.write().map_err(|e| anyhow!(e.to_string()))
    }

    /// Gets the session id
    pub fn id(&self) -> String {
        self.id.clone()
    }

    /// Gets the session status
    pub fn status(&self) -> usize {
        self.status.load(Ordering::Relaxed)
    }

    /// Gets a value by the key
    pub fn get<T: DeserializeOwned>(&self, key: &str) -> Option<T> {
        from_value(self.data().ok()?.get(key).cloned()?).ok()
    }

    /// Sets a value by the key
    pub fn set<T: DeserializeOwned + Serialize>(&self, key: &str, val: T) -> Option<T> {
        let prev = self
            .data_mut()
            .ok()?
            .insert(key.to_string(), to_value(val).ok()?);

        self.status.store(2, Ordering::SeqCst);

        from_value(prev?).ok()
    }

    /// Removes a value
    pub fn remove<T: DeserializeOwned>(&self, key: &str) -> Option<T> {
        let prev = self.data_mut().ok()?.remove(key)?;

        self.status.store(2, Ordering::SeqCst);

        from_value(prev).ok()
    }

    /// Clears the state
    pub fn clear(&self) -> Result<()> {
        self.data_mut()?.clear();
        self.status.store(2, Ordering::SeqCst);
        Ok(())
    }

    /// Saves the current state to the store
    pub async fn save(&self) -> Result<()> {
        if self.status.compare_and_swap(2, 3, Ordering::SeqCst) == 2 {
            let data = self.data()?.clone();
            self.config.set(&self.id, data, self.max_age()).await?;
        }
        Ok(())
    }

    /// Renews the new state
    pub async fn renew(&mut self) -> Result<()> {
        if self.status.load(Ordering::Relaxed) < 4 {
            self.config.remove(&self.id).await?;
            self.id = self.config.generate();
            self.data_mut()?.clear();
            self.status.store(4, Ordering::SeqCst);
        }
        Ok(())
    }

    /// Destroys the current state from store
    pub async fn destroy(&self) -> Result<()> {
        if self.status.load(Ordering::Relaxed) < 5 {
            self.config.remove(&self.id).await?;
            self.status.store(5, Ordering::SeqCst);
        }
        Ok(())
    }
}

impl fmt::Debug for Session {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Session")
            .field("id", &self.id)
            .field("status", &self.status)
            .field("data", &self.data)
            .field("config", &self.config)
            .finish()
    }
}