chore: add missing docs for 0.7 (#3203)

This commit is contained in:
Greg Johnston
2024-11-11 19:58:38 -05:00
committed by GitHub
parent 998eefb8c5
commit a5293f0b79
33 changed files with 928 additions and 545 deletions

View File

@@ -6,6 +6,10 @@ use reactive_graph::{
use server_fn::{error::ServerFnErrorSerde, ServerFn, ServerFnError};
use std::{ops::Deref, panic::Location, sync::Arc};
/// An error that can be caused by a server action.
///
/// This is used for propagating errors from the server to the client when JS/WASM are not
/// supported.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ServerActionError {
path: Arc<str>,
@@ -13,6 +17,7 @@ pub struct ServerActionError {
}
impl ServerActionError {
/// Creates a new error associated with the given path.
pub fn new(path: &str, err: &str) -> Self {
Self {
path: path.into(),
@@ -20,15 +25,18 @@ impl ServerActionError {
}
}
/// The path with which this error is associated.
pub fn path(&self) -> &str {
&self.path
}
/// The error message.
pub fn err(&self) -> &str {
&self.err
}
}
/// An [`ArcAction`] that can be used to call a server function.
pub struct ArcServerAction<S>
where
S: ServerFn + 'static,
@@ -45,6 +53,7 @@ where
S::Output: Send + Sync + 'static,
S::Error: Send + Sync + 'static,
{
/// Creates a new [`ArcAction`] that will call the server function `S` when dispatched.
#[track_caller]
pub fn new() -> Self {
let err = use_context::<ServerActionError>().and_then(|error| {
@@ -116,6 +125,7 @@ where
}
}
/// An [`Action`] that can be used to call a server function.
pub struct ServerAction<S>
where
S: ServerFn + 'static,
@@ -132,6 +142,7 @@ where
S::Output: Send + Sync + 'static,
S::Error: Send + Sync + 'static,
{
/// Creates a new [`Action`] that will call the server function `S` when dispatched.
pub fn new() -> Self {
let err = use_context::<ServerActionError>().and_then(|error| {
(error.path() == S::PATH)

View File

@@ -1,4 +1,6 @@
//#![deny(missing_docs)]
//! Utilities for communicating between the server and the client with [`leptos`].
#![deny(missing_docs)]
#![forbid(unsafe_code)]
mod action;
@@ -13,135 +15,25 @@ pub use once_resource::*;
mod resource;
pub use resource::*;
mod shared;
////! # Leptos Server Functions
////!
////! This package is based on a simple idea: sometimes its useful to write functions
////! that will only run on the server, and call them from the client.
////!
////! If youre creating anything beyond a toy app, youll need to do this all the time:
////! reading from or writing to a database that only runs on the server, running expensive
////! computations using libraries you dont want to ship down to the client, accessing
////! APIs that need to be called from the server rather than the client for CORS reasons
////! or because you need a secret API key thats stored on the server and definitely
////! shouldnt be shipped down to a users browser.
////!
////! Traditionally, this is done by separating your server and client code, and by setting
////! up something like a REST API or GraphQL API to allow your client to fetch and mutate
////! data on the server. This is fine, but it requires you to write and maintain your code
////! in multiple separate places (client-side code for fetching, server-side functions to run),
////! as well as creating a third thing to manage, which is the API contract between the two.
////!
////! This package provides two simple primitives that allow you instead to write co-located,
////! isomorphic server functions. (*Co-located* means you can write them in your app code so
////! that they are “located alongside” the client code that calls them, rather than separating
////! the client and server sides. *Isomorphic* means you can call them from the client as if
////! you were simply calling a function; the function call has the “same shape” on the client
////! as it does on the server.)
////!
////! ### `#[server]`
////!
////! The [`#[server]`](https://docs.rs/leptos/latest/leptos/attr.server.html) macro allows you to annotate a function to
////! indicate that it should only run on the server (i.e., when you have an `ssr` feature in your
////! crate that is enabled).
////!
////! ```rust,ignore
////! use leptos::prelude::*;
////! #[server(ReadFromDB)]
////! async fn read_posts(how_many: usize, query: String) -> Result<Vec<Posts>, ServerFnError> {
////! // do some server-only work here to access the database
////! let posts = todo!();;
////! Ok(posts)
////! }
////!
////! // call the function
////! spawn_local(async {
////! let posts = read_posts(3, "my search".to_string()).await;
////! log::debug!("posts = {posts:#?}");
////! });
////! ```
////!
////! If you call this function from the client, it will serialize the function arguments and `POST`
////! them to the server as if they were the inputs in `<form method="POST">`.
////!
////! Heres what you need to remember:
////! - **Server functions must be `async`.** Even if the work being done inside the function body
////! can run synchronously on the server, from the clients perspective it involves an asynchronous
////! function call.
////! - **Server functions must return `Result<T, ServerFnError>`.** Even if the work being done
////! inside the function body cant fail, the processes of serialization/deserialization and the
////! network call are fallible.
////! - **Return types must be [Serializable](leptos_reactive::Serializable).**
////! This should be fairly obvious: we have to serialize arguments to send them to the server, and we
////! need to deserialize the result to return it to the client.
////! - **Arguments must be implement [serde::Serialize].** They are serialized as an `application/x-www-form-urlencoded`
////! form data using [`serde_qs`](https://docs.rs/serde_qs/latest/serde_qs/) or as `application/cbor`
////! using [`cbor`](https://docs.rs/cbor/latest/cbor/). **Note**: You should explicitly include `serde` with the
////! `derive` feature enabled in your `Cargo.toml`. You can do this by running `cargo add serde --features=derive`.
////! - Context comes from the server. [`use_context`](leptos_reactive::use_context) can be used to access specific
////! server-related data, as documented in the server integrations. This allows accessing things like HTTP request
////! headers as needed. However, server functions *not* have access to reactive state that exists in the client.
////!
////! ## Server Function Encodings
////!
////! By default, the server function call is a `POST` request that serializes the arguments as URL-encoded form data in the body
////! of the request. But there are a few other methods supported. Optionally, we can provide another argument to the `#[server]`
////! macro to specify an alternate encoding:
////!
////! ```rust,ignore
////! #[server(AddTodo, "/api", "Url")]
////! #[server(AddTodo, "/api", "GetJson")]
////! #[server(AddTodo, "/api", "Cbor")]
////! #[server(AddTodo, "/api", "GetCbor")]
////! ```
////!
////! The four options use different combinations of HTTP verbs and encoding methods:
////!
////! | Name | Method | Request | Response |
////! | ----------------- | ------ | ----------- | -------- |
////! | **Url** (default) | POST | URL encoded | JSON |
////! | **GetJson** | GET | URL encoded | JSON |
////! | **Cbor** | POST | CBOR | CBOR |
////! | **GetCbor** | GET | URL encoded | CBOR |
////!
////! In other words, you have two choices:
////!
////! - `GET` or `POST`? This has implications for things like browser or CDN caching; while `POST` requests should not be cached,
////! `GET` requests can be.
////! - Plain text (arguments sent with URL/form encoding, results sent as JSON) or a binary format (CBOR, encoded as a base64
////! string)?
////!
////! ## Why not `PUT` or `DELETE`? Why URL/form encoding, and not JSON?**
////!
////! These are reasonable questions. Much of the web is built on REST API patterns that encourage the use of semantic HTTP
////! methods like `DELETE` to delete an item from a database, and many devs are accustomed to sending data to APIs in the
////! JSON format.
////!
////! The reason we use `POST` or `GET` with URL-encoded data by default is the `<form>` support. For better or for worse,
////! HTML forms dont support `PUT` or `DELETE`, and they dont support sending JSON. This means that if you use anything
////! but a `GET` or `POST` request with URL-encoded data, it can only work once WASM has loaded.
////!
////! The CBOR encoding is supported for historical reasons; an earlier version of server functions used a URL encoding that
////! didnt support nested objects like structs or vectors as server function arguments, which CBOR did. But note that the
////! CBOR forms encounter the same issue as `PUT`, `DELETE`, or JSON: they do not degrade gracefully if the WASM version of
////! your app is not available.
//pub use server_fn::{error::ServerFnErrorErr, ServerFnError};
//mod action;
//mod multi_action;
//pub use action::*;
//pub use multi_action::*;
//extern crate tracing;
use base64::{engine::general_purpose::STANDARD_NO_PAD, DecodeError, Engine};
pub use shared::*;
/// Encodes data into a string.
pub trait IntoEncodedString {
/// Encodes the data.
fn into_encoded_string(self) -> String;
}
/// Decodes data from a string.
pub trait FromEncodedStr {
/// The decoded data.
type DecodedType<'a>: Borrow<Self>;
/// The type of an error encountered during decoding.
type DecodingError;
/// Decodes the string.
fn from_encoded_str(
data: &str,
) -> Result<Self::DecodedType<'_>, Self::DecodingError>;

View File

@@ -17,6 +17,7 @@ use std::{
panic::Location,
};
/// A reference-counted resource that only loads its data locally on the client.
pub struct ArcLocalResource<T> {
data: ArcAsyncDerived<SendWrapper<T>>,
#[cfg(debug_assertions)]
@@ -34,6 +35,10 @@ impl<T> Clone for ArcLocalResource<T> {
}
impl<T> ArcLocalResource<T> {
/// Creates the resource.
///
/// This will only begin loading data if you are on the client (i.e., if you do not have the
/// `ssr` feature activated).
#[track_caller]
pub fn new<Fut>(fetcher: impl Fn() -> Fut + 'static) -> Self
where
@@ -192,6 +197,7 @@ impl<T> Subscriber for ArcLocalResource<T> {
}
}
/// A resource that only loads its data locally on the client.
pub struct LocalResource<T> {
data: AsyncDerived<SendWrapper<T>>,
#[cfg(debug_assertions)]
@@ -207,6 +213,10 @@ impl<T> Clone for LocalResource<T> {
impl<T> Copy for LocalResource<T> {}
impl<T> LocalResource<T> {
/// Creates the resource.
///
/// This will only begin loading data if you are on the client (i.e., if you do not have the
/// `ssr` feature activated).
#[track_caller]
pub fn new<Fut>(fetcher: impl Fn() -> Fut + 'static) -> Self
where

View File

@@ -5,6 +5,7 @@ use reactive_graph::{
use server_fn::{ServerFn, ServerFnError};
use std::{ops::Deref, panic::Location};
/// An [`ArcMultiAction`] that can be used to call a server function.
pub struct ArcServerMultiAction<S>
where
S: ServerFn + 'static,
@@ -21,6 +22,7 @@ where
S::Output: Send + Sync + 'static,
S::Error: Send + Sync + 'static,
{
/// Creates a new [`ArcMultiAction`] which, when dispatched, will call the server function `S`.
#[track_caller]
pub fn new() -> Self {
Self {
@@ -87,6 +89,7 @@ where
}
}
/// A [`MultiAction`] that can be used to call a server function.
pub struct ServerMultiAction<S>
where
S: ServerFn + 'static,
@@ -114,6 +117,7 @@ where
S::Output: Send + Sync + 'static,
S::Error: Send + Sync + 'static,
{
/// Creates a new [`MultiAction`] which, when dispatched, will call the server function `S`.
pub fn new() -> Self {
Self {
inner: MultiAction::new(|input: &S| {

View File

@@ -1,344 +0,0 @@
use leptos_reactive::{
is_suppressing_resource_load, signal_prelude::*, spawn_local, store_value,
untrack, StoredValue,
};
use server_fn::{ServerFn, ServerFnError};
use std::{future::Future, pin::Pin, rc::Rc};
/// An action that synchronizes multiple imperative `async` calls to the reactive system,
/// tracking the progress of each one.
///
/// Where an [Action](crate::Action) fires a single call, a `MultiAction` allows you to
/// keep track of multiple in-flight actions.
///
/// If youre trying to load data by running an `async` function reactively, you probably
/// want to use a [Resource](leptos_reactive::Resource) instead. If youre trying to occasionally
/// run an `async` function in response to something like a user adding a task to a todo list,
/// youre in the right place.
///
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// async fn send_new_todo_to_api(task: String) -> usize {
/// // do something...
/// // return a task id
/// 42
/// }
/// let add_todo = create_multi_action(|task: &String| {
/// // `task` is given as `&String` because its value is available in `input`
/// send_new_todo_to_api(task.clone())
/// });
///
/// # if false {
/// add_todo.dispatch("Buy milk".to_string());
/// add_todo.dispatch("???".to_string());
/// add_todo.dispatch("Profit!!!".to_string());
/// # }
///
/// # runtime.dispose();
/// ```
///
/// The input to the `async` function should always be a single value,
/// but it can be of any type. The argument is always passed by reference to the
/// function, because it is stored in [Submission::input] as well.
///
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// // if there's a single argument, just use that
/// let action1 = create_multi_action(|input: &String| {
/// let input = input.clone();
/// async move { todo!() }
/// });
///
/// // if there are no arguments, use the unit type `()`
/// let action2 = create_multi_action(|input: &()| async { todo!() });
///
/// // if there are multiple arguments, use a tuple
/// let action3 =
/// create_multi_action(|input: &(usize, String)| async { todo!() });
/// # runtime.dispose();
/// ```
pub struct MultiAction<I, O>(StoredValue<MultiActionState<I, O>>)
where
I: 'static,
O: 'static;
impl<I, O> MultiAction<I, O>
where
I: 'static,
O: 'static,
{
}
impl<I, O> Clone for MultiAction<I, O>
where
I: 'static,
O: 'static,
{
fn clone(&self) -> Self {
*self
}
}
impl<I, O> Copy for MultiAction<I, O>
where
I: 'static,
O: 'static,
{
}
impl<I, O> MultiAction<I, O>
where
I: 'static,
O: 'static,
{
/// Calls the `async` function with a reference to the input type as its argument.
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", skip_all)
)]
pub fn dispatch(&self, input: I) {
self.0.with_value(|a| a.dispatch(input))
}
/// The set of all submissions to this multi-action.
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", skip_all)
)]
pub fn submissions(&self) -> ReadSignal<Vec<Submission<I, O>>> {
self.0.with_value(|a| a.submissions())
}
/// The URL associated with the action (typically as part of a server function.)
/// This enables integration with the `MultiActionForm` component in `leptos_router`.
pub fn url(&self) -> Option<String> {
self.0.with_value(|a| a.url.as_ref().cloned())
}
/// How many times an action has successfully resolved.
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", skip_all)
)]
pub fn version(&self) -> RwSignal<usize> {
self.0.with_value(|a| a.version)
}
/// Associates the URL of the given server function with this action.
/// This enables integration with the `MultiActionForm` component in `leptos_router`.
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", skip_all)
)]
pub fn using_server_fn<T: ServerFn>(self) -> Self {
self.0.update_value(|a| {
a.url = Some(T::url().to_string());
});
self
}
}
struct MultiActionState<I, O>
where
I: 'static,
O: 'static,
{
/// How many times an action has successfully resolved.
pub version: RwSignal<usize>,
submissions: RwSignal<Vec<Submission<I, O>>>,
url: Option<String>,
#[allow(clippy::complexity)]
action_fn: Rc<dyn Fn(&I) -> Pin<Box<dyn Future<Output = O>>>>,
}
/// An action that has been submitted by dispatching it to a [MultiAction](crate::MultiAction).
pub struct Submission<I, O>
where
I: 'static,
O: 'static,
{
/// The current argument that was dispatched to the `async` function.
/// `Some` while we are waiting for it to resolve, `None` if it has resolved.
pub input: RwSignal<Option<I>>,
/// The most recent return value of the `async` function.
pub value: RwSignal<Option<O>>,
pub(crate) pending: RwSignal<bool>,
/// Controls this submission has been canceled.
pub canceled: RwSignal<bool>,
}
impl<I, O> Clone for Submission<I, O> {
fn clone(&self) -> Self {
*self
}
}
impl<I, O> Copy for Submission<I, O> {}
impl<I, O> Submission<I, O>
where
I: 'static,
O: 'static,
{
/// Whether this submission is currently waiting to resolve.
pub fn pending(&self) -> ReadSignal<bool> {
self.pending.read_only()
}
/// Cancels the submission, preventing it from resolving.
pub fn cancel(&self) {
self.canceled.set(true);
}
}
impl<I, O> MultiActionState<I, O>
where
I: 'static,
O: 'static,
{
/// Calls the `async` function with a reference to the input type as its argument.
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", skip_all)
)]
pub fn dispatch(&self, input: I) {
if !is_suppressing_resource_load() {
let fut = (self.action_fn)(&input);
let submission = Submission {
input: create_rw_signal(Some(input)),
value: create_rw_signal(None),
pending: create_rw_signal(true),
canceled: create_rw_signal(false),
};
self.submissions.update(|subs| subs.push(submission));
let canceled = submission.canceled;
let input = submission.input;
let pending = submission.pending;
let value = submission.value;
let version = self.version;
spawn_local(async move {
let new_value = fut.await;
let canceled = untrack(move || canceled.get());
if !canceled {
value.set(Some(new_value));
}
input.set(None);
pending.set(false);
version.update(|n| *n += 1);
})
}
}
/// The set of all submissions to this multi-action.
pub fn submissions(&self) -> ReadSignal<Vec<Submission<I, O>>> {
self.submissions.read_only()
}
}
/// Creates an [MultiAction] to synchronize an imperative `async` call to the synchronous reactive system.
///
/// If youre trying to load data by running an `async` function reactively, you probably
/// want to use a [create_resource](leptos_reactive::create_resource) instead. If youre trying
/// to occasionally run an `async` function in response to something like a user clicking a button,
/// you're in the right place.
///
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// async fn send_new_todo_to_api(task: String) -> usize {
/// // do something...
/// // return a task id
/// 42
/// }
/// let add_todo = create_multi_action(|task: &String| {
/// // `task` is given as `&String` because its value is available in `input`
/// send_new_todo_to_api(task.clone())
/// });
/// # if false {
///
/// add_todo.dispatch("Buy milk".to_string());
/// add_todo.dispatch("???".to_string());
/// add_todo.dispatch("Profit!!!".to_string());
///
/// assert_eq!(add_todo.submissions().get().len(), 3);
/// # }
/// # runtime.dispose();
/// ```
///
/// The input to the `async` function should always be a single value,
/// but it can be of any type. The argument is always passed by reference to the
/// function, because it is stored in [Submission::input] as well.
///
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// // if there's a single argument, just use that
/// let action1 = create_multi_action(|input: &String| {
/// let input = input.clone();
/// async move { todo!() }
/// });
///
/// // if there are no arguments, use the unit type `()`
/// let action2 = create_multi_action(|input: &()| async { todo!() });
///
/// // if there are multiple arguments, use a tuple
/// let action3 =
/// create_multi_action(|input: &(usize, String)| async { todo!() });
/// # runtime.dispose();
/// ```
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
pub fn create_multi_action<I, O, F, Fu>(action_fn: F) -> MultiAction<I, O>
where
I: 'static,
O: 'static,
F: Fn(&I) -> Fu + 'static,
Fu: Future<Output = O> + 'static,
{
let version = create_rw_signal(0);
let submissions = create_rw_signal(Vec::new());
let action_fn = Rc::new(move |input: &I| {
let fut = action_fn(input);
Box::pin(fut) as Pin<Box<dyn Future<Output = O>>>
});
MultiAction(store_value(MultiActionState {
version,
submissions,
url: None,
action_fn,
}))
}
/// Creates an [MultiAction] that can be used to call a server function.
///
/// ```rust,ignore
/// # use leptos::*;
///
/// #[server(MyServerFn)]
/// async fn my_server_fn() -> Result<(), ServerFnError> {
/// todo!()
/// }
///
/// # let runtime = create_runtime();
/// let my_server_multi_action = create_server_multi_action::<MyServerFn>();
/// # runtime.dispose();
/// ```
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
pub fn create_server_multi_action<S>(
) -> MultiAction<S, Result<S::Output, ServerFnError<S::Error>>>
where
S: Clone + ServerFn,
{
#[cfg(feature = "ssr")]
let c = move |args: &S| S::run_body(args.clone());
#[cfg(not(feature = "ssr"))]
let c = move |args: &S| S::run_on_client(args.clone());
create_multi_action(c).using_server_fn::<S>()
}

View File

@@ -43,6 +43,15 @@ use std::{
task::{Context, Poll, Waker},
};
/// A reference-counted resource that only loads once.
///
/// Resources allow asynchronously loading data and serializing it from the server to the client,
/// so that it loads on the server, and is then deserialized on the client. This improves
/// performance by beginning data loading on the server when the request is made, rather than
/// beginning it on the client after WASM has been loaded.
///
/// You can access the value of the resource either synchronously using `.get()` or asynchronously
/// using `.await`.
#[derive(Debug)]
pub struct ArcOnceResource<T, Ser = JsonSerdeCodec> {
trigger: ArcTrigger,
@@ -80,6 +89,12 @@ where
<Ser as Encoder<T>>::Encoded: IntoEncodedString,
<Ser as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a new resource with the encoding `Ser`. If `blocking` is `true`, this is a blocking
/// resource.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_with_options(
fut: impl Future<Output = T> + Send + 'static,
@@ -287,11 +302,17 @@ where
<JsonSerdeCodec as Encoder<T>>::Encoded: IntoEncodedString,
<JsonSerdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`JsonSerdeCodec`] for encoding/decoding the value.
#[track_caller]
pub fn new(fut: impl Future<Output = T> + Send + 'static) -> Self {
ArcOnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`JsonSerdeCodec`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_blocking(fut: impl Future<Output = T> + Send + 'static) -> Self {
ArcOnceResource::new_with_options(fut, true)
@@ -307,6 +328,7 @@ T: Send + Sync + 'static,
<FromToStringCodec as Encoder<T>>::Encoded: IntoEncodedString,
<FromToStringCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`FromToStringCodec`] for encoding/decoding the value.
pub fn new_str(
fut: impl Future<Output = T> + Send + 'static
) -> Self
@@ -314,6 +336,11 @@ T: Send + Sync + 'static,
ArcOnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`FromToStringCodec`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
pub fn new_str_blocking(
fut: impl Future<Output = T> + Send + 'static
) -> Self
@@ -332,6 +359,7 @@ T: Send + Sync + 'static,
<JsonSerdeWasmCodec as Encoder<T>>::Encoded: IntoEncodedString,
<JsonSerdeWasmCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`JsonSerdeWasmCodec`] for encoding/decoding the value.
#[track_caller]
pub fn new_serde_wb(
fut: impl Future<Output = T> + Send + 'static
@@ -340,6 +368,11 @@ fut: impl Future<Output = T> + Send + 'static
ArcOnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`JsonSerdeWasmCodec`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_serde_wb_blocking(
fut: impl Future<Output = T> + Send + 'static
@@ -360,6 +393,7 @@ where
<MiniserdeCodec as Encoder<T>>::Encoded: IntoEncodedString,
<MiniserdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`MiniserdeCodec`] for encoding/decoding the value.
#[track_caller]
pub fn new_miniserde(
fut: impl Future<Output = T> + Send + 'static,
@@ -367,6 +401,11 @@ where
ArcOnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`MiniserdeCodec`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_miniserde_blocking(
fut: impl Future<Output = T> + Send + 'static,
@@ -385,6 +424,7 @@ T: Send + Sync + 'static,
<SerdeLite<JsonSerdeCodec> as Encoder<T>>::Encoded: IntoEncodedString,
<SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`SerdeLite`] for encoding/decoding the value.
#[track_caller]
pub fn new_serde_lite(
fut: impl Future<Output = T> + Send + 'static
@@ -393,6 +433,11 @@ fut: impl Future<Output = T> + Send + 'static
ArcOnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`SerdeLite`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_serde_lite_blocking(
fut: impl Future<Output = T> + Send + 'static
@@ -414,11 +459,17 @@ where
<RkyvCodec as Encoder<T>>::Encoded: IntoEncodedString,
<RkyvCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`RkyvCodec`] for encoding/decoding the value.
#[track_caller]
pub fn new_rkyv(fut: impl Future<Output = T> + Send + 'static) -> Self {
ArcOnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`RkyvCodec`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_rkyv_blocking(
fut: impl Future<Output = T> + Send + 'static,
@@ -427,6 +478,15 @@ where
}
}
/// A resource that only loads once.
///
/// Resources allow asynchronously loading data and serializing it from the server to the client,
/// so that it loads on the server, and is then deserialized on the client. This improves
/// performance by beginning data loading on the server when the request is made, rather than
/// beginning it on the client after WASM has been loaded.
///
/// You can access the value of the resource either synchronously using `.get()` or asynchronously
/// using `.await`.
#[derive(Debug)]
pub struct OnceResource<T, Ser = JsonSerdeCodec> {
inner: ArenaItem<ArcOnceResource<T, Ser>>,
@@ -452,6 +512,12 @@ where
<Ser as Encoder<T>>::Encoded: IntoEncodedString,
<Ser as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a new resource with the encoding `Ser`. If `blocking` is `true`, this is a blocking
/// resource.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_with_options(
fut: impl Future<Output = T> + Send + 'static,
@@ -567,11 +633,17 @@ where
<JsonSerdeCodec as Encoder<T>>::Encoded: IntoEncodedString,
<JsonSerdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`JsonSerdeCodec`] for encoding/decoding the value.
#[track_caller]
pub fn new(fut: impl Future<Output = T> + Send + 'static) -> Self {
OnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`JsonSerdeCodec`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_blocking(fut: impl Future<Output = T> + Send + 'static) -> Self {
OnceResource::new_with_options(fut, true)
@@ -587,6 +659,7 @@ T: Send + Sync + 'static,
<FromToStringCodec as Encoder<T>>::Encoded: IntoEncodedString,
<FromToStringCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`FromToStringCodec`] for encoding/decoding the value.
pub fn new_str(
fut: impl Future<Output = T> + Send + 'static
) -> Self
@@ -594,6 +667,11 @@ T: Send + Sync + 'static,
OnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`FromToStringCodec`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
pub fn new_str_blocking(
fut: impl Future<Output = T> + Send + 'static
) -> Self
@@ -612,6 +690,7 @@ T: Send + Sync + 'static,
<JsonSerdeWasmCodec as Encoder<T>>::Encoded: IntoEncodedString,
<JsonSerdeWasmCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`JsonSerdeWasmCodec`] for encoding/decoding the value.
#[track_caller]
pub fn new_serde_wb(
fut: impl Future<Output = T> + Send + 'static
@@ -620,6 +699,11 @@ fut: impl Future<Output = T> + Send + 'static
OnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`JsonSerdeWasmCodec`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_serde_wb_blocking(
fut: impl Future<Output = T> + Send + 'static
@@ -640,6 +724,7 @@ where
<MiniserdeCodec as Encoder<T>>::Encoded: IntoEncodedString,
<MiniserdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`MiniserdeCodec`] for encoding/decoding the value.
#[track_caller]
pub fn new_miniserde(
fut: impl Future<Output = T> + Send + 'static,
@@ -647,6 +732,11 @@ where
OnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`MiniserdeCodec`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_miniserde_blocking(
fut: impl Future<Output = T> + Send + 'static,
@@ -665,6 +755,7 @@ T: Send + Sync + 'static,
<SerdeLite<JsonSerdeCodec> as Encoder<T>>::Encoded: IntoEncodedString,
<SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`SerdeLite`] for encoding/decoding the value.
#[track_caller]
pub fn new_serde_lite(
fut: impl Future<Output = T> + Send + 'static
@@ -673,6 +764,11 @@ fut: impl Future<Output = T> + Send + 'static
OnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`SerdeLite`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_serde_lite_blocking(
fut: impl Future<Output = T> + Send + 'static
@@ -694,11 +790,17 @@ where
<RkyvCodec as Encoder<T>>::Encoded: IntoEncodedString,
<RkyvCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a resource using [`RkyvCodec`] for encoding/decoding the value.
#[track_caller]
pub fn new_rkyv(fut: impl Future<Output = T> + Send + 'static) -> Self {
OnceResource::new_with_options(fut, false)
}
/// Creates a blocking resource using [`RkyvCodec`] for encoding/decoding the value.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_rkyv_blocking(
fut: impl Future<Output = T> + Send + 'static,

View File

@@ -37,9 +37,12 @@ use std::{
pub(crate) static IS_SUPPRESSING_RESOURCE_LOAD: AtomicBool =
AtomicBool::new(false);
/// Used to prevent resources from actually loading, in environments (like server route generation)
/// where they are not needed.
pub struct SuppressResourceLoad;
impl SuppressResourceLoad {
/// Prevents resources from loading until this is dropped.
pub fn new() -> Self {
IS_SUPPRESSING_RESOURCE_LOAD.store(true, Ordering::Relaxed);
Self
@@ -58,6 +61,15 @@ impl Drop for SuppressResourceLoad {
}
}
/// A reference-counted asynchronous resource.
///
/// Resources allow asynchronously loading data and serializing it from the server to the client,
/// so that it loads on the server, and is then deserialized on the client. This improves
/// performance by beginning data loading on the server when the request is made, rather than
/// beginning it on the client after WASM has been loaded.
///
/// You can access the value of the resource either synchronously using `.get()` or asynchronously
/// using `.await`.
pub struct ArcResource<T, Ser = JsonSerdeCodec> {
ser: PhantomData<Ser>,
refetch: ArcRwSignal<usize>,
@@ -194,6 +206,21 @@ where
<Ser as Encoder<T>>::Encoded: IntoEncodedString,
<Ser as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a new resource with the encoding `Ser`.
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// If `blocking` is `true`, this is a blocking resource.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_with_options<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -278,6 +305,8 @@ where
}
}
/// Synchronously, reactively reads the current value of the resource and applies the function
/// `f` to its value if it is `Some(_)`.
#[track_caller]
pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U>
where
@@ -351,6 +380,13 @@ where
T: Send + Sync + 'static,
E: Send + Sync + Clone + 'static,
{
/// Applies the given function when a resource that returns `Result<T, E>`
/// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`
/// calls over the `Option<Result<_, _>>` returned by the resource.
///
/// This is useful when used with features like server functions, in conjunction
/// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are
/// left to handle the `None` and `Err(_)` states.
#[track_caller]
pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {
self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))
@@ -367,6 +403,15 @@ where
<JsonSerdeCodec as Encoder<T>>::Encoded: IntoEncodedString,
<JsonSerdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a new resource with the encoding [`JsonSerdeCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
#[track_caller]
pub fn new<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -380,6 +425,19 @@ where
ArcResource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`JsonSerdeCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -402,6 +460,15 @@ where
<FromToStringCodec as Encoder<T>>::Encoded: IntoEncodedString,
<FromToStringCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a new resource with the encoding [`FromToStringCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
pub fn new_str<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
@@ -414,6 +481,19 @@ where
ArcResource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`FromToStringCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
pub fn new_str_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
@@ -436,6 +516,15 @@ where
<JsonSerdeWasmCodec as Encoder<T>>::Encoded: IntoEncodedString,
<JsonSerdeWasmCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a new resource with the encoding [`JsonSerdeWasmCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
#[track_caller]
pub fn new_serde_wb<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -449,6 +538,19 @@ where
ArcResource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`JsonSerdeWasmCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_serde_wb_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -473,6 +575,15 @@ where
<MiniserdeCodec as Encoder<T>>::Encoded: IntoEncodedString,
<MiniserdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a new resource with the encoding [`MiniserdeCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
#[track_caller]
pub fn new_miniserde<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -486,6 +597,19 @@ where
ArcResource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`MiniserdeCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_miniserde_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -509,6 +633,15 @@ where
<SerdeLite<JsonSerdeCodec> as Encoder<T>>::Encoded: IntoEncodedString,
<SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a new resource with the encoding [`SerdeLite`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
#[track_caller]
pub fn new_serde_lite<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -522,6 +655,19 @@ where
ArcResource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`SerdeLite`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_serde_lite_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -547,6 +693,15 @@ where
<RkyvCodec as Encoder<T>>::Encoded: IntoEncodedString,
<RkyvCodec as Decoder<T>>::Encoded: FromEncodedStr,
{
/// Creates a new resource with the encoding [`RkyvCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
#[track_caller]
pub fn new_rkyv<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -560,6 +715,19 @@ where
ArcResource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`RkyvCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_rkyv_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -590,11 +758,22 @@ impl<T, Ser> ArcResource<T, Ser>
where
T: 'static,
{
/// Returns a new [`Future`] that is ready when the resource has loaded, and accesses its inner
/// value by reference.
pub fn by_ref(&self) -> AsyncDerivedRefFuture<T> {
self.data.by_ref()
}
}
/// An asynchronous resource.
///
/// Resources allow asynchronously loading data and serializing it from the server to the client,
/// so that it loads on the server, and is then deserialized on the client. This improves
/// performance by beginning data loading on the server when the request is made, rather than
/// beginning it on the client after WASM has been loaded.
///
/// You can access the value of the resource either synchronously using `.get()` or asynchronously
/// using `.await`.
pub struct Resource<T, Ser = JsonSerdeCodec>
where
T: Send + Sync + 'static,
@@ -707,6 +886,15 @@ where
<FromToStringCodec as Decoder<T>>::Encoded: FromEncodedStr,
T: Send + Sync,
{
/// Creates a new resource with the encoding [`FromToStringCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
#[track_caller]
pub fn new_str<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -720,6 +908,19 @@ where
Resource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`FromToStringCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_str_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -745,6 +946,15 @@ where
<JsonSerdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
T: Send + Sync,
{
/// Creates a new resource with the encoding [`JsonSerdeCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
#[track_caller]
pub fn new<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -758,6 +968,19 @@ where
Resource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`JsonSerdeCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -782,6 +1005,15 @@ where
<JsonSerdeWasmCodec as Decoder<T>>::Encoded: FromEncodedStr,
T: Send + Sync,
{
/// Creates a new resource with the encoding [`JsonSerdeWasmCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
pub fn new_serde_wb<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
@@ -794,6 +1026,19 @@ where
Resource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`JsonSerdeWasmCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
pub fn new_serde_wb_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
@@ -819,6 +1064,15 @@ where
<MiniserdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
T: Send + Sync,
{
/// Creates a new resource with the encoding [`MiniserdeCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
pub fn new_miniserde<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
@@ -830,6 +1084,31 @@ where
{
Resource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`MiniserdeCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
pub fn new_miniserde_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
) -> Self
where
S: PartialEq + Clone + Send + Sync + 'static,
T: Send + Sync + 'static,
Fut: Future<Output = T> + Send + 'static,
{
Resource::new_with_options(source, fetcher, true)
}
}
#[cfg(feature = "serde-lite")]
@@ -843,6 +1122,15 @@ where
<SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded: FromEncodedStr,
T: Send + Sync,
{
/// Creates a new resource with the encoding [`SerdeLite`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
pub fn new_serde_lite<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
@@ -855,6 +1143,19 @@ where
Resource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`SerdeLite`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
pub fn new_serde_lite_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
@@ -880,6 +1181,15 @@ where
<RkyvCodec as Decoder<T>>::Encoded: FromEncodedStr,
T: Send + Sync,
{
/// Creates a new resource with the encoding [`RkyvCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
pub fn new_rkyv<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
@@ -892,6 +1202,19 @@ where
Resource::new_with_options(source, fetcher, false)
}
/// Creates a new blocking resource with the encoding [`RkyvCodec`].
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
pub fn new_rkyv_blocking<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
@@ -915,6 +1238,21 @@ where
<Ser as Decoder<T>>::Encoded: FromEncodedStr,
T: Send + Sync,
{
/// Creates a new resource with the encoding `Ser`.
///
/// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
/// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
/// generate a new [`Future`] to load data.
///
/// On creation, if you are on the server, this will run the `fetcher` once to generate
/// a `Future` whose value will be serialized from the server to the client. If you are on
/// the client, the initial value will be deserialized without re-running that async task.
///
/// If `blocking` is `true`, this is a blocking resource.
///
/// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
/// This is useful if you need their data to set HTML document metadata or information that
/// needs to appear in HTTP headers.
#[track_caller]
pub fn new_with_options<S, Fut>(
source: impl Fn() -> S + Send + Sync + 'static,
@@ -937,6 +1275,8 @@ where
}
}
/// Synchronously, reactively reads the current value of the resource and applies the function
/// `f` to its value if it is `Some(_)`.
pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U> {
self.data
.try_with(|n| n.as_ref().map(|n| Some(f(n))))?
@@ -961,6 +1301,13 @@ where
T: Send + Sync,
E: Send + Sync + Clone,
{
/// Applies the given function when a resource that returns `Result<T, E>`
/// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`
/// calls over the `Option<Result<_, _>>` returned by the resource.
///
/// This is useful when used with features like server functions, in conjunction
/// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are
/// left to handle the `None` and `Err(_)` states.
#[track_caller]
pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {
self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))
@@ -984,6 +1331,8 @@ impl<T, Ser> Resource<T, Ser>
where
T: Send + Sync + 'static,
{
/// Returns a new [`Future`] that is ready when the resource has loaded, and accesses its inner
/// value by reference.
pub fn by_ref(&self) -> AsyncDerivedRefFuture<T> {
self.data.by_ref()
}

View File

@@ -31,6 +31,7 @@ pub struct SharedValue<T, Ser = JsonSerdeCodec> {
}
impl<T, Ser> SharedValue<T, Ser> {
/// Returns the inner value.
pub fn into_inner(self) -> T {
self.value
}
@@ -46,6 +47,12 @@ where
<<JsonSerdeCodec as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
Debug,
{
/// Wraps the initial value.
///
/// If this is on the server, the function will be invoked and the value serialized. When it runs
/// on the client, it will be deserialized without running the function again.
///
/// This uses the [`JsonSerdeCodec`] encoding.
pub fn new(initial: impl FnOnce() -> T) -> Self {
SharedValue::new_with_encoding(initial)
}
@@ -61,6 +68,12 @@ where
<<FromToStringCodec as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
Debug,
{
/// Wraps the initial value.
///
/// If this is on the server, the function will be invoked and the value serialized. When it runs
/// on the client, it will be deserialized without running the function again.
///
/// This uses the [`FromToStringCodec`] encoding.
pub fn new_str(initial: impl FnOnce() -> T) -> Self {
SharedValue::new_with_encoding(initial)
}
@@ -77,7 +90,13 @@ where
<<SerdeLite<JsonSerdeCodec> as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
Debug,
{
pub fn new(initial: impl FnOnce() -> T) -> Self {
/// Wraps the initial value.
///
/// If this is on the server, the function will be invoked and the value serialized. When it runs
/// on the client, it will be deserialized without running the function again.
///
/// This uses the [`SerdeLite`] encoding.
pub fn new_serde_lite(initial: impl FnOnce() -> T) -> Self {
SharedValue::new_with_encoding(initial)
}
}
@@ -93,7 +112,13 @@ where
<<JsonSerdeWasmCodec as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
Debug,
{
pub fn new(initial: impl FnOnce() -> T) -> Self {
/// Wraps the initial value.
///
/// If this is on the server, the function will be invoked and the value serialized. When it runs
/// on the client, it will be deserialized without running the function again.
///
/// This uses the [`JsonSerdeWasmCodec`] encoding.
pub fn new_serde_wb(initial: impl FnOnce() -> T) -> Self {
SharedValue::new_with_encoding(initial)
}
}
@@ -109,7 +134,13 @@ where
<<MiniserdeCodec as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
Debug,
{
pub fn new(initial: impl FnOnce() -> T) -> Self {
/// Wraps the initial value.
///
/// If this is on the server, the function will be invoked and the value serialized. When it runs
/// on the client, it will be deserialized without running the function again.
///
/// This uses the [`MiniserdeCodec`] encoding.
pub fn new_miniserde(initial: impl FnOnce() -> T) -> Self {
SharedValue::new_with_encoding(initial)
}
}
@@ -125,7 +156,13 @@ where
<<RkyvCodec as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
Debug,
{
pub fn new(initial: impl FnOnce() -> T) -> Self {
/// Wraps the initial value.
///
/// If this is on the server, the function will be invoked and the value serialized. When it runs
/// on the client, it will be deserialized without running the function again.
///
/// This uses the [`RkyvCodec`] encoding.
pub fn new_rkyv(initial: impl FnOnce() -> T) -> Self {
SharedValue::new_with_encoding(initial)
}
}
@@ -140,6 +177,12 @@ where
<<Ser as codee::Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
Debug,
{
/// Wraps the initial value.
///
/// If this is on the server, the function will be invoked and the value serialized. When it runs
/// on the client, it will be deserialized without running the function again.
///
/// This uses `Ser` as an encoding.
pub fn new_with_encoding(initial: impl FnOnce() -> T) -> Self {
let value: T;
#[cfg(feature = "hydration")]