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}