Skip to main content

topcoat_session/
state.rs

1use tokio::sync::Mutex;
2use topcoat_core::{
3    context::{Cx, request_context},
4    error::Result,
5};
6
7use crate::{Token, token_store};
8
9/// The request-scoped session cell, registered on the request context by the
10/// session layer (or manually via `CxTestBuilder` in tests).
11///
12/// The cell caches the token presented by the request so the token store is
13/// read at most once per request, and it is updated by the lifecycle
14/// functions ([`start`](crate::start), [`stop`](crate::stop),
15/// [`rotate`](crate::rotate)) so later reads within the same request observe
16/// the change.
17#[derive(Debug, Default)]
18pub struct SessionState {
19    token: Mutex<Load>,
20}
21
22#[derive(Debug, Default)]
23enum Load {
24    #[default]
25    Unloaded,
26    Loaded(Option<Token>),
27}
28
29impl SessionState {
30    /// Creates an empty cell; the token is loaded on first access.
31    #[must_use]
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    /// Returns the request's current token, reading the token store on first
37    /// access. Concurrent callers share the one read.
38    pub(crate) async fn token(&self, cx: &Cx) -> Result<Option<Token>> {
39        let mut load = self.token.lock().await;
40        if let Load::Loaded(token) = &*load {
41            return Ok(token.clone());
42        }
43        let token = token_store(cx).read(cx).await?;
44        *load = Load::Loaded(token.clone());
45        Ok(token)
46    }
47
48    /// Replaces the request's view of the token after a lifecycle change.
49    pub(crate) async fn set(&self, token: Option<Token>) {
50        *self.token.lock().await = Load::Loaded(token);
51    }
52}
53
54pub(crate) fn state(cx: &Cx) -> &SessionState {
55    request_context(cx)
56}