topcoat_core/context.rs
1mod context_map;
2mod id;
3
4pub use context_map::*;
5pub use id::*;
6
7use std::{any::Any, ops::Deref, sync::Arc};
8
9use crate::{abort::AbortStore, memoize::MemoizeCache};
10
11/// The request context.
12///
13/// Pages, layouts, components, and routes can take `cx: &Cx` as an optional
14/// parameter when they need request-scoped information; Topcoat passes it
15/// automatically. Use it to read values registered for the request with the
16/// app and request context helpers, such as [`app_context`] and
17/// [`request_context`].
18#[derive(Debug, Default)]
19pub struct Cx {
20 id: CxId,
21 app_context: Arc<ContextMap>,
22 request_context: ContextMap,
23 memoize_cache: MemoizeCache,
24 abort_store: AbortStore,
25}
26
27impl Cx {
28 /// Creates a `Cx` from the given app and request context maps.
29 fn new(app_context: Arc<ContextMap>, request_context: ContextMap) -> Self {
30 Self {
31 id: CxId::new(),
32 app_context,
33 request_context,
34 memoize_cache: MemoizeCache::new(),
35 abort_store: AbortStore::new(),
36 }
37 }
38
39 /// Returns this context's unique [`CxId`].
40 #[inline]
41 pub fn id(&self) -> CxId {
42 self.id
43 }
44}
45
46/// Assembles the request context for an in-flight request.
47///
48/// The router creates a `CxBuilder` over the shared app context, then threads
49/// `&mut CxBuilder` through the layers wrapping the matched route so each can
50/// register request-scoped values with [`insert`](Self::insert) before the
51/// route runs. Because a `CxBuilder` dereferences to the [`Cx`] it is building,
52/// app and request context can be read through it (with helpers such as
53/// [`app_context`] and [`request_context`]) while it is still being assembled.
54#[derive(Debug, Default)]
55pub struct CxBuilder {
56 cx: Cx,
57}
58
59impl CxBuilder {
60 /// Creates a builder over the shared app context, with an empty request
61 /// context.
62 #[must_use]
63 pub fn new(app_context: Arc<ContextMap>) -> Self {
64 Self {
65 cx: Cx::new(app_context, ContextMap::new()),
66 }
67 }
68
69 /// Registers `value` on the request context, returning the value previously
70 /// registered for `T`, if any.
71 ///
72 /// A type can hold only one value at a time, so registering a type that is
73 /// already present replaces it and hands back the displaced value.
74 pub fn insert<T>(&mut self, value: T) -> Option<T>
75 where
76 T: Any + Send + Sync,
77 {
78 self.cx.request_context.insert(value)
79 }
80
81 /// Returns `true` if a value of type `T` has been registered on the request
82 /// context.
83 #[must_use]
84 pub fn contains<T>(&self) -> bool
85 where
86 T: Any + Send + Sync,
87 {
88 self.cx.request_context.contains::<T>()
89 }
90
91 /// Returns a reference to the request context value of type `T`, or `None`
92 /// if no such value has been registered.
93 #[must_use]
94 pub fn get<T>(&self) -> Option<&T>
95 where
96 T: Any + Send + Sync,
97 {
98 self.cx.request_context.get::<T>()
99 }
100
101 /// Returns a mutable reference to the request context value of type `T`, or
102 /// `None` if no such value has been registered.
103 #[must_use]
104 pub fn get_mut<T>(&mut self) -> Option<&mut T>
105 where
106 T: Any + Send + Sync,
107 {
108 self.cx.request_context.get_mut::<T>()
109 }
110
111 /// Consumes the builder, returning the finished [`Cx`].
112 #[must_use]
113 pub fn build(self) -> Cx {
114 self.cx
115 }
116}
117
118impl Deref for CxBuilder {
119 type Target = Cx;
120
121 fn deref(&self) -> &Cx {
122 &self.cx
123 }
124}
125
126/// Assembles a [`Cx`] from scratch, for tests.
127///
128/// Unlike [`CxBuilder`], which only configures request context over an existing
129/// shared app context, `CxTestBuilder` populates both app and request context.
130#[derive(Debug, Default)]
131pub struct CxTestBuilder {
132 app_context: ContextMap,
133 request_context: ContextMap,
134}
135
136impl CxTestBuilder {
137 /// Creates an empty builder.
138 #[must_use]
139 pub fn new() -> Self {
140 Self::default()
141 }
142
143 /// Registers `value` on the app context.
144 #[must_use]
145 pub fn app_context<T>(mut self, value: T) -> Self
146 where
147 T: Any + Send + Sync,
148 {
149 self.app_context.insert(value);
150 self
151 }
152
153 /// Registers `value` on the request context.
154 #[must_use]
155 pub fn request_context<T>(mut self, value: T) -> Self
156 where
157 T: Any + Send + Sync,
158 {
159 self.request_context.insert(value);
160 self
161 }
162
163 /// Consumes the builder, returning the assembled [`Cx`].
164 #[must_use]
165 pub fn build(self) -> Cx {
166 Cx::new(Arc::new(self.app_context), self.request_context)
167 }
168}
169
170#[inline]
171#[doc(hidden)]
172pub fn memoize_cache(cx: &Cx) -> &MemoizeCache {
173 &cx.memoize_cache
174}
175
176#[inline]
177#[doc(hidden)]
178pub fn abort_store(cx: &Cx) -> &AbortStore {
179 &cx.abort_store
180}