Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ members = [
"examples/context",
"examples/cookie",
"examples/datastar",
"examples/deferred-view",
"examples/font",
"examples/hello-world",
"examples/htmx",
Expand Down
72 changes: 64 additions & 8 deletions crates/topcoat-core/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ use crate::{abort::AbortStore, memoize::MemoizeCache};
pub struct Cx {
id: CxId,
app_context: Arc<ContextMap>,
request_context: ContextMap,
memoize_cache: MemoizeCache,
abort_store: AbortStore,
request_context: Arc<ContextMap>,
memoize_cache: Arc<MemoizeCache>,
abort_store: Arc<AbortStore>,
}

impl Cx {
Expand All @@ -30,17 +30,59 @@ impl Cx {
Self {
id: CxId::new(),
app_context,
request_context,
memoize_cache: MemoizeCache::new(),
abort_store: AbortStore::new(),
request_context: Arc::new(request_context),
memoize_cache: Arc::new(MemoizeCache::new()),
abort_store: Arc::new(AbortStore::new()),
}
}

/// Returns this context's unique [`CxId`].
#[inline]
#[must_use]
pub fn id(&self) -> CxId {
self.id
}

/// Returns an owned handle to this request context.
///
/// The handle keeps request and app context values alive while a response
/// stream is still producing deferred views.
#[must_use]
pub fn handle(&self) -> CxHandle {
CxHandle(Self {
id: self.id,
app_context: Arc::clone(&self.app_context),
request_context: Arc::clone(&self.request_context),
memoize_cache: Arc::clone(&self.memoize_cache),
abort_store: Arc::clone(&self.abort_store),
})
}
}

/// An owned handle to a request [`Cx`].
///
/// It dereferences to `Cx`, so context helpers accept `&handle`.
#[derive(Debug)]
pub struct CxHandle(Cx);

impl Clone for CxHandle {
fn clone(&self) -> Self {
self.0.handle()
}
}

impl AsRef<Cx> for CxHandle {
fn as_ref(&self) -> &Cx {
&self.0
}
}

impl Deref for CxHandle {
type Target = Cx;

fn deref(&self) -> &Self::Target {
&self.0
}
}

/// Assembles the request context for an in-flight request.
Expand Down Expand Up @@ -71,11 +113,17 @@ impl CxBuilder {
///
/// A type can hold only one value at a time, so registering a type that is
/// already present replaces it and hands back the displaced value.
///
/// # Panics
///
/// Panics if the request context was shared before the builder finished.
pub fn insert<T>(&mut self, value: T) -> Option<T>
where
T: Any + Send + Sync,
{
self.cx.request_context.insert(value)
Arc::get_mut(&mut self.cx.request_context)
.expect("request context was shared before it was built")
.insert(value)
}

/// Returns `true` if a value of type `T` has been registered on the request
Expand All @@ -100,12 +148,18 @@ impl CxBuilder {

/// Returns a mutable reference to the request context value of type `T`, or
/// `None` if no such value has been registered.
///
/// # Panics
///
/// Panics if the request context was shared before the builder finished.
#[must_use]
pub fn get_mut<T>(&mut self) -> Option<&mut T>
where
T: Any + Send + Sync,
{
self.cx.request_context.get_mut::<T>()
Arc::get_mut(&mut self.cx.request_context)
.expect("request context was shared before it was built")
.get_mut::<T>()
}

/// Consumes the builder, returning the finished [`Cx`].
Expand Down Expand Up @@ -169,12 +223,14 @@ impl CxTestBuilder {

#[inline]
#[doc(hidden)]
#[must_use]
pub fn memoize_cache(cx: &Cx) -> &MemoizeCache {
&cx.memoize_cache
}

#[inline]
#[doc(hidden)]
#[must_use]
pub fn abort_store(cx: &Cx) -> &AbortStore {
&cx.abort_store
}
2 changes: 2 additions & 0 deletions crates/topcoat-core/src/context/context_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ where
/// }
/// ```
#[track_caller]
#[must_use]
pub fn app_context<T>(cx: &Cx) -> &T
where
T: Any + Send + Sync,
Expand Down Expand Up @@ -131,6 +132,7 @@ where
/// }
/// ```
#[track_caller]
#[must_use]
pub fn request_context<T>(cx: &Cx) -> &T
where
T: Any + Send + Sync,
Expand Down
3 changes: 1 addition & 2 deletions crates/topcoat-router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ discover = [
"dep:inventory",
]
multipart = [
"dep:futures-util",
"dep:multer",
]
serve = [
Expand Down Expand Up @@ -56,7 +55,7 @@ bytes.workspace = true
form_urlencoded.workspace = true
futures-core.workspace = true
futures-sink = { workspace = true, optional = true }
futures-util = { workspace = true, optional = true }
futures-util.workspace = true
heck.workspace = true
http.workspace = true
http-body.workspace = true
Expand Down
2 changes: 2 additions & 0 deletions crates/topcoat-router/src/page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ impl PageFn {
}

/// Renders the page, returning a [`Result`].
#[must_use]
pub fn render<'cx>(
&self,
cx: &'cx Cx,
Expand Down Expand Up @@ -124,6 +125,7 @@ impl LayoutFn {
}

/// Renders the layout, embedding the given child content [`Result`]`<`[`View`]`>` as its slot.
#[must_use]
pub fn render<'cx>(
&self,
cx: &'cx Cx,
Expand Down
Loading
Loading