Skip to main content

vantage_table/
write_context.rs

1//! Who is making a write, carried to the table's lifecycle hooks.
2//!
3//! A host (an API server handling a request, a desktop app acting for its
4//! user) wraps the work in [`WriteContext::scope`]; every write awaited inside
5//! it hands the context to its hooks through their `context` field. Vantage
6//! doesn't interpret it: `principal` is whatever record the host identifies the
7//! actor with, and `group` ties together everything one request or action run
8//! caused.
9
10use std::future::Future;
11
12tokio::task_local! {
13    static CONTEXT: WriteContext;
14}
15
16/// Built with [`WriteContext::new`] and the `with_*` builders, so a field
17/// added later (request data, say) doesn't break existing hosts.
18#[derive(Clone, Debug, Default, PartialEq)]
19#[non_exhaustive]
20pub struct WriteContext {
21    /// The actor, as a CBOR map (name plus attributes).
22    pub principal: Option<ciborium::Value>,
23    /// Shared by every write one request or action run causes.
24    pub group: Option<String>,
25}
26
27impl WriteContext {
28    /// An empty context: no principal, no group.
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    pub fn with_principal(mut self, principal: ciborium::Value) -> Self {
34        self.principal = Some(principal);
35        self
36    }
37
38    pub fn with_group(mut self, group: impl Into<String>) -> Self {
39        self.group = Some(group.into());
40        self
41    }
42
43    /// Run `fut` with this context in scope.
44    pub async fn scope<F: Future>(self, fut: F) -> F::Output {
45        CONTEXT.scope(self, fut).await
46    }
47
48    /// The context in scope for the current task, if any.
49    pub fn current() -> Option<WriteContext> {
50        CONTEXT.try_with(|c| c.clone()).ok()
51    }
52}