leptos_server/
resource.rs

1use crate::{FromEncodedStr, IntoEncodedString};
2#[cfg(feature = "rkyv")]
3use codee::binary::RkyvCodec;
4#[cfg(feature = "serde-wasm-bindgen")]
5use codee::string::JsonSerdeWasmCodec;
6#[cfg(feature = "miniserde")]
7use codee::string::MiniserdeCodec;
8#[cfg(feature = "serde-lite")]
9use codee::SerdeLite;
10use codee::{
11    string::{FromToStringCodec, JsonSerdeCodec},
12    Decoder, Encoder,
13};
14use core::{fmt::Debug, marker::PhantomData};
15use futures::Future;
16use hydration_context::{SerializedDataId, SharedContext};
17use reactive_graph::{
18    computed::{
19        ArcAsyncDerived, ArcMemo, AsyncDerived, AsyncDerivedFuture,
20        AsyncDerivedRefFuture,
21    },
22    graph::{Source, ToAnySubscriber},
23    owner::Owner,
24    prelude::*,
25    signal::{ArcRwSignal, RwSignal},
26};
27use std::{
28    future::{pending, IntoFuture},
29    ops::{Deref, DerefMut},
30    panic::Location,
31    sync::{
32        atomic::{AtomicBool, Ordering},
33        Arc,
34    },
35};
36
37pub(crate) static IS_SUPPRESSING_RESOURCE_LOAD: AtomicBool =
38    AtomicBool::new(false);
39
40/// Used to prevent resources from actually loading, in environments (like server route generation)
41/// where they are not needed.
42pub struct SuppressResourceLoad;
43
44impl SuppressResourceLoad {
45    /// Prevents resources from loading until this is dropped.
46    pub fn new() -> Self {
47        IS_SUPPRESSING_RESOURCE_LOAD.store(true, Ordering::Relaxed);
48        Self
49    }
50}
51
52impl Default for SuppressResourceLoad {
53    fn default() -> Self {
54        Self::new()
55    }
56}
57
58impl Drop for SuppressResourceLoad {
59    fn drop(&mut self) {
60        IS_SUPPRESSING_RESOURCE_LOAD.store(false, Ordering::Relaxed);
61    }
62}
63
64/// A reference-counted asynchronous resource.
65///
66/// Resources allow asynchronously loading data and serializing it from the server to the client,
67/// so that it loads on the server, and is then deserialized on the client. This improves
68/// performance by beginning data loading on the server when the request is made, rather than
69/// beginning it on the client after WASM has been loaded.
70///
71/// You can access the value of the resource either synchronously using `.get()` or asynchronously
72/// using `.await`.
73pub struct ArcResource<T, Ser = JsonSerdeCodec> {
74    ser: PhantomData<Ser>,
75    refetch: ArcRwSignal<usize>,
76    data: ArcAsyncDerived<T>,
77    #[cfg(any(debug_assertions, leptos_debuginfo))]
78    defined_at: &'static Location<'static>,
79}
80
81impl<T, Ser> Debug for ArcResource<T, Ser> {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        let mut d = f.debug_struct("ArcResource");
84        d.field("ser", &self.ser).field("data", &self.data);
85        #[cfg(any(debug_assertions, leptos_debuginfo))]
86        d.field("defined_at", self.defined_at);
87        d.finish_non_exhaustive()
88    }
89}
90
91impl<T, Ser> From<ArcResource<T, Ser>> for Resource<T, Ser>
92where
93    T: Send + Sync,
94{
95    #[track_caller]
96    fn from(arc_resource: ArcResource<T, Ser>) -> Self {
97        Resource {
98            ser: PhantomData,
99            data: arc_resource.data.into(),
100            refetch: arc_resource.refetch.into(),
101            #[cfg(any(debug_assertions, leptos_debuginfo))]
102            defined_at: Location::caller(),
103        }
104    }
105}
106
107impl<T, Ser> From<Resource<T, Ser>> for ArcResource<T, Ser>
108where
109    T: Send + Sync,
110{
111    #[track_caller]
112    fn from(resource: Resource<T, Ser>) -> Self {
113        ArcResource {
114            ser: PhantomData,
115            data: resource.data.into(),
116            refetch: resource.refetch.into(),
117            #[cfg(any(debug_assertions, leptos_debuginfo))]
118            defined_at: Location::caller(),
119        }
120    }
121}
122
123impl<T, Ser> DefinedAt for ArcResource<T, Ser> {
124    fn defined_at(&self) -> Option<&'static Location<'static>> {
125        #[cfg(any(debug_assertions, leptos_debuginfo))]
126        {
127            Some(self.defined_at)
128        }
129        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
130        {
131            None
132        }
133    }
134}
135
136impl<T, Ser> Clone for ArcResource<T, Ser> {
137    fn clone(&self) -> Self {
138        Self {
139            ser: self.ser,
140            refetch: self.refetch.clone(),
141            data: self.data.clone(),
142            #[cfg(any(debug_assertions, leptos_debuginfo))]
143            defined_at: self.defined_at,
144        }
145    }
146}
147
148impl<T, Ser> Deref for ArcResource<T, Ser> {
149    type Target = ArcAsyncDerived<T>;
150
151    fn deref(&self) -> &Self::Target {
152        &self.data
153    }
154}
155
156impl<T, Ser> Track for ArcResource<T, Ser>
157where
158    T: 'static,
159{
160    fn track(&self) {
161        self.data.track();
162    }
163}
164
165impl<T, Ser> Notify for ArcResource<T, Ser>
166where
167    T: 'static,
168{
169    fn notify(&self) {
170        self.data.notify()
171    }
172}
173
174impl<T, Ser> Write for ArcResource<T, Ser>
175where
176    T: 'static,
177{
178    type Value = Option<T>;
179
180    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
181        self.data.try_write()
182    }
183
184    fn try_write_untracked(
185        &self,
186    ) -> Option<impl DerefMut<Target = Self::Value>> {
187        self.data.try_write_untracked()
188    }
189}
190
191impl<T, Ser> ReadUntracked for ArcResource<T, Ser>
192where
193    T: 'static,
194{
195    type Value = <ArcAsyncDerived<T> as ReadUntracked>::Value;
196
197    #[track_caller]
198    fn try_read_untracked(&self) -> Option<Self::Value> {
199        #[cfg(all(feature = "hydration", debug_assertions))]
200        {
201            use reactive_graph::{
202                computed::suspense::SuspenseContext, effect::in_effect_scope,
203                owner::use_context,
204            };
205            if !in_effect_scope() && use_context::<SuspenseContext>().is_none()
206            {
207                let location = std::panic::Location::caller();
208                reactive_graph::log_warning(format_args!(
209                    "At {location}, you are reading a resource in `hydrate` \
210                     mode outside a <Suspense/> or <Transition/> or effect. \
211                     This can cause hydration mismatch errors and loses out \
212                     on a significant performance optimization. To fix this \
213                     issue, you can either: \n1. Wrap the place where you \
214                     read the resource in a <Suspense/> or <Transition/> \
215                     component, or \n2. Switch to using \
216                     ArcLocalResource::new(), which will wait to load the \
217                     resource until the app is hydrated on the client side. \
218                     (This will have worse performance in most cases.)",
219                ));
220            }
221        }
222        self.data.try_read_untracked()
223    }
224}
225
226impl<T, Ser> ArcResource<T, Ser>
227where
228    Ser: Encoder<T> + Decoder<T>,
229    <Ser as Encoder<T>>::Error: Debug,
230    <Ser as Decoder<T>>::Error: Debug,
231    <<Ser as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,
232    <Ser as Encoder<T>>::Encoded: IntoEncodedString,
233    <Ser as Decoder<T>>::Encoded: FromEncodedStr,
234{
235    /// Creates a new resource with the encoding `Ser`.
236    ///
237    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
238    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
239    /// generate a new [`Future`] to load data.
240    ///
241    /// On creation, if you are on the server, this will run the `fetcher` once to generate
242    /// a `Future` whose value will be serialized from the server to the client. If you are on
243    /// the client, the initial value will be deserialized without re-running that async task.
244    ///
245    /// If `blocking` is `true`, this is a blocking resource.
246    ///
247    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
248    /// This is useful if you need their data to set HTML document metadata or information that
249    /// needs to appear in HTTP headers.
250    #[track_caller]
251    pub fn new_with_options<S, Fut>(
252        source: impl Fn() -> S + Send + Sync + 'static,
253        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
254        #[allow(unused)] // this is used with `feature = "ssr"`
255        blocking: bool,
256    ) -> ArcResource<T, Ser>
257    where
258        S: PartialEq + Clone + Send + Sync + 'static,
259        T: Send + Sync + 'static,
260        Fut: Future<Output = T> + Send + 'static,
261    {
262        let shared_context = Owner::current_shared_context();
263        let id = shared_context
264            .as_ref()
265            .map(|sc| sc.next_id())
266            .unwrap_or_default();
267
268        let initial = initial_value::<T, Ser>(&id, shared_context.as_ref());
269        let is_ready = initial.is_some();
270
271        let refetch = ArcRwSignal::new(0);
272        let source = ArcMemo::new({
273            let refetch = refetch.clone();
274            move |_| (refetch.get(), source())
275        });
276        let fun = {
277            let source = source.clone();
278            move || {
279                let (_, source) = source.get();
280                let fut = fetcher(source);
281                async move {
282                    if IS_SUPPRESSING_RESOURCE_LOAD.load(Ordering::Relaxed) {
283                        pending().await
284                    } else {
285                        fut.await
286                    }
287                }
288            }
289        };
290
291        let data = ArcAsyncDerived::new_with_manual_dependencies(
292            initial, fun, &source,
293        );
294        if is_ready {
295            source.with_untracked(|_| ());
296            source.add_subscriber(data.to_any_subscriber());
297        }
298
299        #[cfg(feature = "ssr")]
300        if let Some(shared_context) = shared_context {
301            let value = data.clone();
302            let ready_fut = data.ready();
303
304            if blocking {
305                shared_context.defer_stream(Box::pin(data.ready()));
306            }
307
308            if shared_context.get_is_hydrating() {
309                shared_context.write_async(
310                    id,
311                    Box::pin(async move {
312                        ready_fut.await;
313                        value.with_untracked(|data| match &data {
314                            // TODO handle serialization errors
315                            Some(val) => {
316                                Ser::encode(val).unwrap().into_encoded_string()
317                            }
318                            _ => unreachable!(),
319                        })
320                    }),
321                );
322            }
323        }
324
325        ArcResource {
326            ser: PhantomData,
327            data,
328            refetch,
329            #[cfg(any(debug_assertions, leptos_debuginfo))]
330            defined_at: Location::caller(),
331        }
332    }
333
334    /// Synchronously, reactively reads the current value of the resource and applies the function
335    /// `f` to its value if it is `Some(_)`.
336    #[track_caller]
337    pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U>
338    where
339        T: Send + Sync + 'static,
340    {
341        self.data.try_with(|n| n.as_ref().map(f))?
342    }
343
344    /// Re-runs the async function with the current source data.
345    pub fn refetch(&self) {
346        *self.refetch.write() += 1;
347    }
348}
349
350#[inline(always)]
351#[allow(unused)]
352pub(crate) fn initial_value<T, Ser>(
353    id: &SerializedDataId,
354    shared_context: Option<&Arc<dyn SharedContext + Send + Sync>>,
355) -> Option<T>
356where
357    Ser: Encoder<T> + Decoder<T>,
358    <Ser as Encoder<T>>::Error: Debug,
359    <Ser as Decoder<T>>::Error: Debug,
360    <<Ser as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,
361    <Ser as Encoder<T>>::Encoded: IntoEncodedString,
362    <Ser as Decoder<T>>::Encoded: FromEncodedStr,
363{
364    #[cfg(feature = "hydration")]
365    {
366        use std::borrow::Borrow;
367
368        let shared_context = Owner::current_shared_context();
369        if let Some(shared_context) = shared_context {
370            let value = shared_context.read_data(id);
371            if let Some(value) = value {
372                let encoded =
373                    match <Ser as Decoder<T>>::Encoded::from_encoded_str(&value)
374                    {
375                        Ok(value) => value,
376                        Err(e) => {
377                            #[cfg(feature = "tracing")]
378                            tracing::error!("couldn't deserialize: {e:?}");
379                            return None;
380                        }
381                    };
382                let encoded = encoded.borrow();
383                match Ser::decode(encoded) {
384                    Ok(value) => return Some(value),
385                    #[allow(unused)]
386                    Err(e) => {
387                        #[cfg(feature = "tracing")]
388                        tracing::error!("couldn't deserialize: {e:?}");
389                    }
390                }
391            }
392        }
393    }
394    None
395}
396
397impl<T, E, Ser> ArcResource<Result<T, E>, Ser>
398where
399    Ser: Encoder<Result<T, E>> + Decoder<Result<T, E>>,
400    <Ser as Encoder<Result<T, E>>>::Error: Debug,
401    <Ser as Decoder<Result<T, E>>>::Error: Debug,
402    <<Ser as Decoder<Result<T, E>>>::Encoded as FromEncodedStr>::DecodingError:
403        Debug,
404    <Ser as Encoder<Result<T, E>>>::Encoded: IntoEncodedString,
405    <Ser as Decoder<Result<T, E>>>::Encoded: FromEncodedStr,
406    T: Send + Sync + 'static,
407    E: Send + Sync + Clone + 'static,
408{
409    /// Applies the given function when a resource that returns `Result<T, E>`
410    /// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`
411    /// calls over the `Option<Result<_, _>>` returned by the resource.
412    ///
413    /// This is useful when used with features like server functions, in conjunction
414    /// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are
415    /// left to handle the `None` and `Err(_)` states.
416    #[track_caller]
417    pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {
418        self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))
419    }
420}
421
422impl<T> ArcResource<T, JsonSerdeCodec>
423where
424    JsonSerdeCodec: Encoder<T> + Decoder<T>,
425    <JsonSerdeCodec as Encoder<T>>::Error: Debug,
426    <JsonSerdeCodec as Decoder<T>>::Error: Debug,
427    <<JsonSerdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
428        Debug,
429    <JsonSerdeCodec as Encoder<T>>::Encoded: IntoEncodedString,
430    <JsonSerdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
431{
432    /// Creates a new resource with the encoding [`JsonSerdeCodec`].
433    ///
434    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
435    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
436    /// generate a new [`Future`] to load data.
437    ///
438    /// On creation, if you are on the server, this will run the `fetcher` once to generate
439    /// a `Future` whose value will be serialized from the server to the client. If you are on
440    /// the client, the initial value will be deserialized without re-running that async task.
441    #[track_caller]
442    pub fn new<S, Fut>(
443        source: impl Fn() -> S + Send + Sync + 'static,
444        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
445    ) -> Self
446    where
447        S: PartialEq + Clone + Send + Sync + 'static,
448        T: Send + Sync + 'static,
449        Fut: Future<Output = T> + Send + 'static,
450    {
451        ArcResource::new_with_options(source, fetcher, false)
452    }
453
454    /// Creates a new blocking resource with the encoding [`JsonSerdeCodec`].
455    ///
456    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
457    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
458    /// generate a new [`Future`] to load data.
459    ///
460    /// On creation, if you are on the server, this will run the `fetcher` once to generate
461    /// a `Future` whose value will be serialized from the server to the client. If you are on
462    /// the client, the initial value will be deserialized without re-running that async task.
463    ///
464    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
465    /// This is useful if you need their data to set HTML document metadata or information that
466    /// needs to appear in HTTP headers.
467    #[track_caller]
468    pub fn new_blocking<S, Fut>(
469        source: impl Fn() -> S + Send + Sync + 'static,
470        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
471    ) -> Self
472    where
473        S: PartialEq + Clone + Send + Sync + 'static,
474        T: Send + Sync + 'static,
475        Fut: Future<Output = T> + Send + 'static,
476    {
477        ArcResource::new_with_options(source, fetcher, true)
478    }
479}
480
481impl<T> ArcResource<T, FromToStringCodec>
482where
483    FromToStringCodec: Encoder<T> + Decoder<T>,
484    <FromToStringCodec as Encoder<T>>::Error: Debug, <FromToStringCodec as Decoder<T>>::Error: Debug,
485    <<FromToStringCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,
486    <FromToStringCodec as Encoder<T>>::Encoded: IntoEncodedString,
487    <FromToStringCodec as Decoder<T>>::Encoded: FromEncodedStr,
488{
489    /// Creates a new resource with the encoding [`FromToStringCodec`].
490    ///
491    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
492    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
493    /// generate a new [`Future`] to load data.
494    ///
495    /// On creation, if you are on the server, this will run the `fetcher` once to generate
496    /// a `Future` whose value will be serialized from the server to the client. If you are on
497    /// the client, the initial value will be deserialized without re-running that async task.
498    pub fn new_str<S, Fut>(
499        source: impl Fn() -> S + Send + Sync + 'static,
500        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
501    ) -> Self
502    where
503        S: PartialEq + Clone + Send + Sync + 'static,
504        T: Send + Sync + 'static,
505        Fut: Future<Output = T> + Send + 'static,
506    {
507        ArcResource::new_with_options(source, fetcher, false)
508    }
509
510    /// Creates a new blocking resource with the encoding [`FromToStringCodec`].
511    ///
512    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
513    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
514    /// generate a new [`Future`] to load data.
515    ///
516    /// On creation, if you are on the server, this will run the `fetcher` once to generate
517    /// a `Future` whose value will be serialized from the server to the client. If you are on
518    /// the client, the initial value will be deserialized without re-running that async task.
519    ///
520    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
521    /// This is useful if you need their data to set HTML document metadata or information that
522    /// needs to appear in HTTP headers.
523    pub fn new_str_blocking<S, Fut>(
524        source: impl Fn() -> S + Send + Sync + 'static,
525        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
526    ) -> Self
527    where
528        S: PartialEq + Clone + Send + Sync + 'static,
529        T: Send + Sync + 'static,
530        Fut: Future<Output = T> + Send + 'static,
531    {
532        ArcResource::new_with_options(source, fetcher, true)
533    }
534}
535
536#[cfg(feature = "serde-wasm-bindgen")]
537impl<T> ArcResource<T, JsonSerdeWasmCodec>
538where
539    JsonSerdeWasmCodec: Encoder<T> + Decoder<T>,
540    <JsonSerdeWasmCodec as Encoder<T>>::Error: Debug, <JsonSerdeWasmCodec as Decoder<T>>::Error: Debug,
541    <<JsonSerdeWasmCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,
542    <JsonSerdeWasmCodec as Encoder<T>>::Encoded: IntoEncodedString,
543    <JsonSerdeWasmCodec as Decoder<T>>::Encoded: FromEncodedStr,
544{
545    /// Creates a new resource with the encoding [`JsonSerdeWasmCodec`].
546    ///
547    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
548    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
549    /// generate a new [`Future`] to load data.
550    ///
551    /// On creation, if you are on the server, this will run the `fetcher` once to generate
552    /// a `Future` whose value will be serialized from the server to the client. If you are on
553    /// the client, the initial value will be deserialized without re-running that async task.
554    #[track_caller]
555    pub fn new_serde_wb<S, Fut>(
556        source: impl Fn() -> S + Send + Sync + 'static,
557        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
558    ) -> Self
559    where
560        S: PartialEq + Clone + Send + Sync + 'static,
561        T: Send + Sync + 'static,
562        Fut: Future<Output = T> + Send + 'static,
563    {
564        ArcResource::new_with_options(source, fetcher, false)
565    }
566
567    /// Creates a new blocking resource with the encoding [`JsonSerdeWasmCodec`].
568    ///
569    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
570    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
571    /// generate a new [`Future`] to load data.
572    ///
573    /// On creation, if you are on the server, this will run the `fetcher` once to generate
574    /// a `Future` whose value will be serialized from the server to the client. If you are on
575    /// the client, the initial value will be deserialized without re-running that async task.
576    ///
577    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
578    /// This is useful if you need their data to set HTML document metadata or information that
579    /// needs to appear in HTTP headers.
580    #[track_caller]
581    pub fn new_serde_wb_blocking<S, Fut>(
582        source: impl Fn() -> S + Send + Sync + 'static,
583        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
584    ) -> Self
585    where
586        S: PartialEq + Clone + Send + Sync + 'static,
587        T: Send + Sync + 'static,
588        Fut: Future<Output = T> + Send + 'static,
589    {
590        ArcResource::new_with_options(source, fetcher, true)
591    }
592}
593#[cfg(feature = "miniserde")]
594impl<T> ArcResource<T, MiniserdeCodec>
595where
596    MiniserdeCodec: Encoder<T> + Decoder<T>,
597    <MiniserdeCodec as Encoder<T>>::Error: Debug,
598    <MiniserdeCodec as Decoder<T>>::Error: Debug,
599    <<MiniserdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
600        Debug,
601    <MiniserdeCodec as Encoder<T>>::Encoded: IntoEncodedString,
602    <MiniserdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
603{
604    /// Creates a new resource with the encoding [`MiniserdeCodec`].
605    ///
606    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
607    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
608    /// generate a new [`Future`] to load data.
609    ///
610    /// On creation, if you are on the server, this will run the `fetcher` once to generate
611    /// a `Future` whose value will be serialized from the server to the client. If you are on
612    /// the client, the initial value will be deserialized without re-running that async task.
613    #[track_caller]
614    pub fn new_miniserde<S, Fut>(
615        source: impl Fn() -> S + Send + Sync + 'static,
616        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
617    ) -> Self
618    where
619        S: PartialEq + Clone + Send + Sync + 'static,
620        T: Send + Sync + 'static,
621        Fut: Future<Output = T> + Send + 'static,
622    {
623        ArcResource::new_with_options(source, fetcher, false)
624    }
625
626    /// Creates a new blocking resource with the encoding [`MiniserdeCodec`].
627    ///
628    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
629    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
630    /// generate a new [`Future`] to load data.
631    ///
632    /// On creation, if you are on the server, this will run the `fetcher` once to generate
633    /// a `Future` whose value will be serialized from the server to the client. If you are on
634    /// the client, the initial value will be deserialized without re-running that async task.
635    ///
636    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
637    /// This is useful if you need their data to set HTML document metadata or information that
638    /// needs to appear in HTTP headers.
639    #[track_caller]
640    pub fn new_miniserde_blocking<S, Fut>(
641        source: impl Fn() -> S + Send + Sync + 'static,
642        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
643    ) -> Self
644    where
645        S: PartialEq + Clone + Send + Sync + 'static,
646        T: Send + Sync + 'static,
647        Fut: Future<Output = T> + Send + 'static,
648    {
649        ArcResource::new_with_options(source, fetcher, true)
650    }
651}
652
653#[cfg(feature = "serde-lite")]
654impl<T> ArcResource<T, SerdeLite<JsonSerdeCodec>>
655where
656    SerdeLite<JsonSerdeCodec>: Encoder<T> + Decoder<T>,
657    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Error: Debug, <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Error: Debug,
658    <<SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,
659    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Encoded: IntoEncodedString,
660    <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded: FromEncodedStr,
661{
662    /// Creates a new resource with the encoding [`SerdeLite`].
663    ///
664    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
665    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
666    /// generate a new [`Future`] to load data.
667    ///
668    /// On creation, if you are on the server, this will run the `fetcher` once to generate
669    /// a `Future` whose value will be serialized from the server to the client. If you are on
670    /// the client, the initial value will be deserialized without re-running that async task.
671    #[track_caller]
672    pub fn new_serde_lite<S, Fut>(
673        source: impl Fn() -> S + Send + Sync + 'static,
674        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
675    ) -> Self
676    where
677        S: PartialEq + Clone + Send + Sync + 'static,
678        T: Send + Sync + 'static,
679        Fut: Future<Output = T> + Send + 'static,
680    {
681        ArcResource::new_with_options(source, fetcher, false)
682    }
683
684    /// Creates a new blocking resource with the encoding [`SerdeLite`].
685    ///
686    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
687    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
688    /// generate a new [`Future`] to load data.
689    ///
690    /// On creation, if you are on the server, this will run the `fetcher` once to generate
691    /// a `Future` whose value will be serialized from the server to the client. If you are on
692    /// the client, the initial value will be deserialized without re-running that async task.
693    ///
694    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
695    /// This is useful if you need their data to set HTML document metadata or information that
696    /// needs to appear in HTTP headers.
697    #[track_caller]
698    pub fn new_serde_lite_blocking<S, Fut>(
699        source: impl Fn() -> S + Send + Sync + 'static,
700        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
701    ) -> Self
702    where
703        S: PartialEq + Clone + Send + Sync + 'static,
704        T: Send + Sync + 'static,
705        Fut: Future<Output = T> + Send + 'static,
706    {
707        ArcResource::new_with_options(source, fetcher, true)
708    }
709}
710
711#[cfg(feature = "rkyv")]
712impl<T> ArcResource<T, RkyvCodec>
713where
714    RkyvCodec: Encoder<T> + Decoder<T>,
715    <RkyvCodec as Encoder<T>>::Error: Debug,
716    <RkyvCodec as Decoder<T>>::Error: Debug,
717    <<RkyvCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
718        Debug,
719    <RkyvCodec as Encoder<T>>::Encoded: IntoEncodedString,
720    <RkyvCodec as Decoder<T>>::Encoded: FromEncodedStr,
721{
722    /// Creates a new resource with the encoding [`RkyvCodec`].
723    ///
724    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
725    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
726    /// generate a new [`Future`] to load data.
727    ///
728    /// On creation, if you are on the server, this will run the `fetcher` once to generate
729    /// a `Future` whose value will be serialized from the server to the client. If you are on
730    /// the client, the initial value will be deserialized without re-running that async task.
731    #[track_caller]
732    pub fn new_rkyv<S, Fut>(
733        source: impl Fn() -> S + Send + Sync + 'static,
734        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
735    ) -> Self
736    where
737        S: PartialEq + Clone + Send + Sync + 'static,
738        T: Send + Sync + 'static,
739        Fut: Future<Output = T> + Send + 'static,
740    {
741        ArcResource::new_with_options(source, fetcher, false)
742    }
743
744    /// Creates a new blocking resource with the encoding [`RkyvCodec`].
745    ///
746    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
747    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
748    /// generate a new [`Future`] to load data.
749    ///
750    /// On creation, if you are on the server, this will run the `fetcher` once to generate
751    /// a `Future` whose value will be serialized from the server to the client. If you are on
752    /// the client, the initial value will be deserialized without re-running that async task.
753    ///
754    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
755    /// This is useful if you need their data to set HTML document metadata or information that
756    /// needs to appear in HTTP headers.
757    #[track_caller]
758    pub fn new_rkyv_blocking<S, Fut>(
759        source: impl Fn() -> S + Send + Sync + 'static,
760        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
761    ) -> Self
762    where
763        S: PartialEq + Clone + Send + Sync + 'static,
764        T: Send + Sync + 'static,
765        Fut: Future<Output = T> + Send + 'static,
766    {
767        ArcResource::new_with_options(source, fetcher, true)
768    }
769}
770
771impl<T, Ser> IntoFuture for ArcResource<T, Ser>
772where
773    T: Clone + 'static,
774{
775    type Output = T;
776    type IntoFuture = AsyncDerivedFuture<T>;
777
778    fn into_future(self) -> Self::IntoFuture {
779        self.data.into_future()
780    }
781}
782
783impl<T, Ser> ArcResource<T, Ser>
784where
785    T: 'static,
786{
787    /// Returns a new [`Future`] that is ready when the resource has loaded, and accesses its inner
788    /// value by reference.
789    pub fn by_ref(&self) -> AsyncDerivedRefFuture<T> {
790        self.data.by_ref()
791    }
792}
793
794/// An asynchronous resource.
795///
796/// Resources allow asynchronously loading data and serializing it from the server to the client,
797/// so that it loads on the server, and is then deserialized on the client. This improves
798/// performance by beginning data loading on the server when the request is made, rather than
799/// beginning it on the client after WASM has been loaded.
800///
801/// You can access the value of the resource either synchronously using `.get()` or asynchronously
802/// using `.await`.
803pub struct Resource<T, Ser = JsonSerdeCodec>
804where
805    T: Send + Sync + 'static,
806{
807    ser: PhantomData<Ser>,
808    data: AsyncDerived<T>,
809    refetch: RwSignal<usize>,
810    #[cfg(any(debug_assertions, leptos_debuginfo))]
811    defined_at: &'static Location<'static>,
812}
813
814impl<T, Ser> Debug for Resource<T, Ser>
815where
816    T: Send + Sync + 'static,
817{
818    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
819        let mut d = f.debug_struct("ArcResource");
820        d.field("ser", &self.ser).field("data", &self.data);
821        #[cfg(any(debug_assertions, leptos_debuginfo))]
822        d.field("defined_at", self.defined_at);
823        d.finish_non_exhaustive()
824    }
825}
826
827impl<T, Ser> DefinedAt for Resource<T, Ser>
828where
829    T: Send + Sync + 'static,
830{
831    fn defined_at(&self) -> Option<&'static Location<'static>> {
832        #[cfg(any(debug_assertions, leptos_debuginfo))]
833        {
834            Some(self.defined_at)
835        }
836        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
837        {
838            None
839        }
840    }
841}
842
843impl<T: Send + Sync + 'static, Ser> Copy for Resource<T, Ser> {}
844
845impl<T: Send + Sync + 'static, Ser> Clone for Resource<T, Ser> {
846    fn clone(&self) -> Self {
847        *self
848    }
849}
850
851impl<T, Ser> Deref for Resource<T, Ser>
852where
853    T: Send + Sync + 'static,
854{
855    type Target = AsyncDerived<T>;
856
857    fn deref(&self) -> &Self::Target {
858        &self.data
859    }
860}
861
862impl<T, Ser> Track for Resource<T, Ser>
863where
864    T: Send + Sync + 'static,
865{
866    fn track(&self) {
867        self.data.track();
868    }
869}
870
871impl<T, Ser> Notify for Resource<T, Ser>
872where
873    T: Send + Sync + 'static,
874{
875    fn notify(&self) {
876        self.data.notify()
877    }
878}
879
880impl<T, Ser> Write for Resource<T, Ser>
881where
882    T: Send + Sync + 'static,
883{
884    type Value = Option<T>;
885
886    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
887        self.data.try_write()
888    }
889
890    fn try_write_untracked(
891        &self,
892    ) -> Option<impl DerefMut<Target = Self::Value>> {
893        self.data.try_write_untracked()
894    }
895}
896
897impl<T, Ser> ReadUntracked for Resource<T, Ser>
898where
899    T: Send + Sync + 'static,
900{
901    type Value = <AsyncDerived<T> as ReadUntracked>::Value;
902
903    #[track_caller]
904    fn try_read_untracked(&self) -> Option<Self::Value> {
905        #[cfg(all(feature = "hydration", debug_assertions))]
906        {
907            use reactive_graph::{
908                computed::suspense::SuspenseContext, effect::in_effect_scope,
909                owner::use_context,
910            };
911            if !in_effect_scope() && use_context::<SuspenseContext>().is_none()
912            {
913                let location = std::panic::Location::caller();
914                reactive_graph::log_warning(format_args!(
915                    "At {location}, you are reading a resource in `hydrate` \
916                     mode outside a <Suspense/> or <Transition/> or effect. \
917                     This can cause hydration mismatch errors and loses out \
918                     on a significant performance optimization. To fix this \
919                     issue, you can either: \n1. Wrap the place where you \
920                     read the resource in a <Suspense/> or <Transition/> \
921                     component, or \n2. Switch to using LocalResource::new(), \
922                     which will wait to load the resource until the app is \
923                     hydrated on the client side. (This will have worse \
924                     performance in most cases.)",
925                ));
926            }
927        }
928        self.data.try_read_untracked()
929    }
930}
931
932impl<T> Resource<T, FromToStringCodec>
933where
934    FromToStringCodec: Encoder<T> + Decoder<T>,
935    <FromToStringCodec as Encoder<T>>::Error: Debug, <FromToStringCodec as Decoder<T>>::Error: Debug,
936    <<FromToStringCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,
937    <FromToStringCodec as Encoder<T>>::Encoded: IntoEncodedString,
938    <FromToStringCodec as Decoder<T>>::Encoded: FromEncodedStr,
939    T: Send + Sync,
940{
941    /// Creates a new resource with the encoding [`FromToStringCodec`].
942    ///
943    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
944    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
945    /// generate a new [`Future`] to load data.
946    ///
947    /// On creation, if you are on the server, this will run the `fetcher` once to generate
948    /// a `Future` whose value will be serialized from the server to the client. If you are on
949    /// the client, the initial value will be deserialized without re-running that async task.
950    #[track_caller]
951    pub fn new_str<S, Fut>(
952        source: impl Fn() -> S + Send + Sync + 'static,
953        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
954    ) -> Self
955    where
956        S: PartialEq + Clone + Send + Sync + 'static,
957        T: Send + Sync + 'static,
958        Fut: Future<Output = T> + Send + 'static,
959    {
960        Resource::new_with_options(source, fetcher, false)
961    }
962
963    /// Creates a new blocking resource with the encoding [`FromToStringCodec`].
964    ///
965    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
966    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
967    /// generate a new [`Future`] to load data.
968    ///
969    /// On creation, if you are on the server, this will run the `fetcher` once to generate
970    /// a `Future` whose value will be serialized from the server to the client. If you are on
971    /// the client, the initial value will be deserialized without re-running that async task.
972    ///
973    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
974    /// This is useful if you need their data to set HTML document metadata or information that
975    /// needs to appear in HTTP headers.
976    #[track_caller]
977    pub fn new_str_blocking<S, Fut>(
978        source: impl Fn() -> S + Send + Sync + 'static,
979        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
980    ) -> Self
981    where
982        S: PartialEq + Clone + Send + Sync + 'static,
983        T: Send + Sync + 'static,
984        Fut: Future<Output = T> + Send + 'static,
985    {
986        Resource::new_with_options(source, fetcher, true)
987    }
988}
989
990impl<T> Resource<T, JsonSerdeCodec>
991where
992    JsonSerdeCodec: Encoder<T> + Decoder<T>,
993    <JsonSerdeCodec as Encoder<T>>::Error: Debug,
994    <JsonSerdeCodec as Decoder<T>>::Error: Debug,
995    <<JsonSerdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
996        Debug,
997    <JsonSerdeCodec as Encoder<T>>::Encoded: IntoEncodedString,
998    <JsonSerdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
999    T: Send + Sync,
1000{
1001    /// Creates a new resource with the encoding [`JsonSerdeCodec`].
1002    ///
1003    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
1004    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
1005    /// generate a new [`Future`] to load data.
1006    ///
1007    /// On creation, if you are on the server, this will run the `fetcher` once to generate
1008    /// a `Future` whose value will be serialized from the server to the client. If you are on
1009    /// the client, the initial value will be deserialized without re-running that async task.
1010    #[track_caller]
1011    pub fn new<S, Fut>(
1012        source: impl Fn() -> S + Send + Sync + 'static,
1013        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
1014    ) -> Self
1015    where
1016        S: PartialEq + Clone + Send + Sync + 'static,
1017        T: Send + Sync + 'static,
1018        Fut: Future<Output = T> + Send + 'static,
1019    {
1020        Resource::new_with_options(source, fetcher, false)
1021    }
1022
1023    /// Creates a new blocking resource with the encoding [`JsonSerdeCodec`].
1024    ///
1025    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
1026    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
1027    /// generate a new [`Future`] to load data.
1028    ///
1029    /// On creation, if you are on the server, this will run the `fetcher` once to generate
1030    /// a `Future` whose value will be serialized from the server to the client. If you are on
1031    /// the client, the initial value will be deserialized without re-running that async task.
1032    ///
1033    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
1034    /// This is useful if you need their data to set HTML document metadata or information that
1035    /// needs to appear in HTTP headers.
1036    #[track_caller]
1037    pub fn new_blocking<S, Fut>(
1038        source: impl Fn() -> S + Send + Sync + 'static,
1039        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
1040    ) -> Self
1041    where
1042        S: PartialEq + Clone + Send + Sync + 'static,
1043        T: Send + Sync + 'static,
1044        Fut: Future<Output = T> + Send + 'static,
1045    {
1046        Resource::new_with_options(source, fetcher, true)
1047    }
1048}
1049
1050#[cfg(feature = "serde-wasm-bindgen")]
1051impl<T> Resource<T, JsonSerdeWasmCodec>
1052where
1053    JsonSerdeWasmCodec: Encoder<T> + Decoder<T>,
1054    <JsonSerdeWasmCodec as Encoder<T>>::Error: Debug, <JsonSerdeWasmCodec as Decoder<T>>::Error: Debug,
1055    <<JsonSerdeWasmCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,
1056    <JsonSerdeWasmCodec as Encoder<T>>::Encoded: IntoEncodedString,
1057    <JsonSerdeWasmCodec as Decoder<T>>::Encoded: FromEncodedStr,
1058    T: Send + Sync,
1059{
1060    /// Creates a new resource with the encoding [`JsonSerdeWasmCodec`].
1061    ///
1062    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
1063    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
1064    /// generate a new [`Future`] to load data.
1065    ///
1066    /// On creation, if you are on the server, this will run the `fetcher` once to generate
1067    /// a `Future` whose value will be serialized from the server to the client. If you are on
1068    /// the client, the initial value will be deserialized without re-running that async task.
1069    pub fn new_serde_wb<S, Fut>(
1070        source: impl Fn() -> S + Send + Sync + 'static,
1071        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
1072    ) -> Self
1073    where
1074        S: PartialEq + Clone + Send + Sync + 'static,
1075        T: Send + Sync + 'static,
1076        Fut: Future<Output = T> + Send + 'static,
1077    {
1078        Resource::new_with_options(source, fetcher, false)
1079    }
1080
1081    /// Creates a new blocking resource with the encoding [`JsonSerdeWasmCodec`].
1082    ///
1083    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
1084    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
1085    /// generate a new [`Future`] to load data.
1086    ///
1087    /// On creation, if you are on the server, this will run the `fetcher` once to generate
1088    /// a `Future` whose value will be serialized from the server to the client. If you are on
1089    /// the client, the initial value will be deserialized without re-running that async task.
1090    ///
1091    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
1092    /// This is useful if you need their data to set HTML document metadata or information that
1093    /// needs to appear in HTTP headers.
1094    pub fn new_serde_wb_blocking<S, Fut>(
1095        source: impl Fn() -> S + Send + Sync + 'static,
1096        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
1097    ) -> Self
1098    where
1099        S: PartialEq + Clone + Send + Sync + 'static,
1100        T: Send + Sync + 'static,
1101        Fut: Future<Output = T> + Send + 'static,
1102    {
1103        Resource::new_with_options(source, fetcher, true)
1104    }
1105}
1106
1107#[cfg(feature = "miniserde")]
1108impl<T> Resource<T, MiniserdeCodec>
1109where
1110    MiniserdeCodec: Encoder<T> + Decoder<T>,
1111    <MiniserdeCodec as Encoder<T>>::Error: Debug,
1112    <MiniserdeCodec as Decoder<T>>::Error: Debug,
1113    <<MiniserdeCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
1114        Debug,
1115    <MiniserdeCodec as Encoder<T>>::Encoded: IntoEncodedString,
1116    <MiniserdeCodec as Decoder<T>>::Encoded: FromEncodedStr,
1117    T: Send + Sync,
1118{
1119    /// Creates a new resource with the encoding [`MiniserdeCodec`].
1120    ///
1121    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
1122    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
1123    /// generate a new [`Future`] to load data.
1124    ///
1125    /// On creation, if you are on the server, this will run the `fetcher` once to generate
1126    /// a `Future` whose value will be serialized from the server to the client. If you are on
1127    /// the client, the initial value will be deserialized without re-running that async task.
1128    pub fn new_miniserde<S, Fut>(
1129        source: impl Fn() -> S + Send + Sync + 'static,
1130        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
1131    ) -> Self
1132    where
1133        S: PartialEq + Clone + Send + Sync + 'static,
1134        T: Send + Sync + 'static,
1135        Fut: Future<Output = T> + Send + 'static,
1136    {
1137        Resource::new_with_options(source, fetcher, false)
1138    }
1139
1140    /// Creates a new blocking resource with the encoding [`MiniserdeCodec`].
1141    ///
1142    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
1143    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
1144    /// generate a new [`Future`] to load data.
1145    ///
1146    /// On creation, if you are on the server, this will run the `fetcher` once to generate
1147    /// a `Future` whose value will be serialized from the server to the client. If you are on
1148    /// the client, the initial value will be deserialized without re-running that async task.
1149    ///
1150    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
1151    /// This is useful if you need their data to set HTML document metadata or information that
1152    /// needs to appear in HTTP headers.
1153    pub fn new_miniserde_blocking<S, Fut>(
1154        source: impl Fn() -> S + Send + Sync + 'static,
1155        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
1156    ) -> Self
1157    where
1158        S: PartialEq + Clone + Send + Sync + 'static,
1159        T: Send + Sync + 'static,
1160        Fut: Future<Output = T> + Send + 'static,
1161    {
1162        Resource::new_with_options(source, fetcher, true)
1163    }
1164}
1165
1166#[cfg(feature = "serde-lite")]
1167impl<T> Resource<T, SerdeLite<JsonSerdeCodec>>
1168where
1169    SerdeLite<JsonSerdeCodec>: Encoder<T> + Decoder<T>,
1170    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Error: Debug, <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Error: Debug,
1171    <<SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
1172        Debug,
1173    <SerdeLite<JsonSerdeCodec> as Encoder<T>>::Encoded: IntoEncodedString,
1174    <SerdeLite<JsonSerdeCodec> as Decoder<T>>::Encoded: FromEncodedStr,
1175    T: Send + Sync,
1176{
1177    /// Creates a new resource with the encoding [`SerdeLite`].
1178    ///
1179    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
1180    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
1181    /// generate a new [`Future`] to load data.
1182    ///
1183    /// On creation, if you are on the server, this will run the `fetcher` once to generate
1184    /// a `Future` whose value will be serialized from the server to the client. If you are on
1185    /// the client, the initial value will be deserialized without re-running that async task.
1186    pub fn new_serde_lite<S, Fut>(
1187        source: impl Fn() -> S + Send + Sync + 'static,
1188        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
1189    ) -> Self
1190    where
1191        S: PartialEq + Clone + Send + Sync + 'static,
1192        T: Send + Sync + 'static,
1193        Fut: Future<Output = T> + Send + 'static,
1194    {
1195        Resource::new_with_options(source, fetcher, false)
1196    }
1197
1198    /// Creates a new blocking resource with the encoding [`SerdeLite`].
1199    ///
1200    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
1201    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
1202    /// generate a new [`Future`] to load data.
1203    ///
1204    /// On creation, if you are on the server, this will run the `fetcher` once to generate
1205    /// a `Future` whose value will be serialized from the server to the client. If you are on
1206    /// the client, the initial value will be deserialized without re-running that async task.
1207    ///
1208    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
1209    /// This is useful if you need their data to set HTML document metadata or information that
1210    /// needs to appear in HTTP headers.
1211    pub fn new_serde_lite_blocking<S, Fut>(
1212        source: impl Fn() -> S + Send + Sync + 'static,
1213        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
1214    ) -> Self
1215    where
1216        S: PartialEq + Clone + Send + Sync + 'static,
1217        T: Send + Sync + 'static,
1218        Fut: Future<Output = T> + Send + 'static,
1219    {
1220        Resource::new_with_options(source, fetcher, true)
1221    }
1222}
1223
1224#[cfg(feature = "rkyv")]
1225impl<T> Resource<T, RkyvCodec>
1226where
1227    RkyvCodec: Encoder<T> + Decoder<T>,
1228    <RkyvCodec as Encoder<T>>::Error: Debug,
1229    <RkyvCodec as Decoder<T>>::Error: Debug,
1230    <<RkyvCodec as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError:
1231        Debug,
1232    <RkyvCodec as Encoder<T>>::Encoded: IntoEncodedString,
1233    <RkyvCodec as Decoder<T>>::Encoded: FromEncodedStr,
1234    T: Send + Sync,
1235{
1236    /// Creates a new resource with the encoding [`RkyvCodec`].
1237    ///
1238    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
1239    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
1240    /// generate a new [`Future`] to load data.
1241    ///
1242    /// On creation, if you are on the server, this will run the `fetcher` once to generate
1243    /// a `Future` whose value will be serialized from the server to the client. If you are on
1244    /// the client, the initial value will be deserialized without re-running that async task.
1245    pub fn new_rkyv<S, Fut>(
1246        source: impl Fn() -> S + Send + Sync + 'static,
1247        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
1248    ) -> Self
1249    where
1250        S: PartialEq + Clone + Send + Sync + 'static,
1251        T: Send + Sync + 'static,
1252        Fut: Future<Output = T> + Send + 'static,
1253    {
1254        Resource::new_with_options(source, fetcher, false)
1255    }
1256
1257    /// Creates a new blocking resource with the encoding [`RkyvCodec`].
1258    ///
1259    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
1260    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
1261    /// generate a new [`Future`] to load data.
1262    ///
1263    /// On creation, if you are on the server, this will run the `fetcher` once to generate
1264    /// a `Future` whose value will be serialized from the server to the client. If you are on
1265    /// the client, the initial value will be deserialized without re-running that async task.
1266    ///
1267    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
1268    /// This is useful if you need their data to set HTML document metadata or information that
1269    /// needs to appear in HTTP headers.
1270    pub fn new_rkyv_blocking<S, Fut>(
1271        source: impl Fn() -> S + Send + Sync + 'static,
1272        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
1273    ) -> Self
1274    where
1275        S: PartialEq + Clone + Send + Sync + 'static,
1276        T: Send + Sync + 'static,
1277        Fut: Future<Output = T> + Send + 'static,
1278    {
1279        Resource::new_with_options(source, fetcher, true)
1280    }
1281}
1282
1283impl<T, Ser> Resource<T, Ser>
1284where
1285    Ser: Encoder<T> + Decoder<T>,
1286    <Ser as Encoder<T>>::Error: Debug,
1287    <Ser as Decoder<T>>::Error: Debug,
1288    <<Ser as Decoder<T>>::Encoded as FromEncodedStr>::DecodingError: Debug,
1289    <Ser as Encoder<T>>::Encoded: IntoEncodedString,
1290    <Ser as Decoder<T>>::Encoded: FromEncodedStr,
1291    T: Send + Sync,
1292{
1293    /// Creates a new resource with the encoding `Ser`.
1294    ///
1295    /// This takes a `source` function and a `fetcher`. The resource memoizes and reactively tracks
1296    /// the value returned by `source`. Whenever that value changes, it will run the `fetcher` to
1297    /// generate a new [`Future`] to load data.
1298    ///
1299    /// On creation, if you are on the server, this will run the `fetcher` once to generate
1300    /// a `Future` whose value will be serialized from the server to the client. If you are on
1301    /// the client, the initial value will be deserialized without re-running that async task.
1302    ///
1303    /// If `blocking` is `true`, this is a blocking resource.
1304    ///
1305    /// Blocking resources prevent any of the HTTP response from being sent until they have loaded.
1306    /// This is useful if you need their data to set HTML document metadata or information that
1307    /// needs to appear in HTTP headers.
1308    #[track_caller]
1309    pub fn new_with_options<S, Fut>(
1310        source: impl Fn() -> S + Send + Sync + 'static,
1311        fetcher: impl Fn(S) -> Fut + Send + Sync + 'static,
1312        blocking: bool,
1313    ) -> Resource<T, Ser>
1314    where
1315        S: Send + Sync + Clone + PartialEq + 'static,
1316        T: Send + Sync + 'static,
1317        Fut: Future<Output = T> + Send + 'static,
1318    {
1319        let ArcResource { data, refetch, .. }: ArcResource<T, Ser> =
1320            ArcResource::new_with_options(source, fetcher, blocking);
1321        Resource {
1322            ser: PhantomData,
1323            data: data.into(),
1324            refetch: refetch.into(),
1325            #[cfg(any(debug_assertions, leptos_debuginfo))]
1326            defined_at: Location::caller(),
1327        }
1328    }
1329
1330    /// Synchronously, reactively reads the current value of the resource and applies the function
1331    /// `f` to its value if it is `Some(_)`.
1332    pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U> {
1333        self.data
1334            .try_with(|n| n.as_ref().map(|n| Some(f(n))))?
1335            .flatten()
1336    }
1337
1338    /// Re-runs the async function with the current source data.
1339    pub fn refetch(&self) {
1340        self.refetch.try_update(|n| *n += 1);
1341    }
1342}
1343
1344impl<T, E, Ser> Resource<Result<T, E>, Ser>
1345where
1346    Ser: Encoder<Result<T, E>> + Decoder<Result<T, E>>,
1347    <Ser as Encoder<Result<T, E>>>::Error: Debug,
1348    <Ser as Decoder<Result<T, E>>>::Error: Debug,
1349    <<Ser as Decoder<Result<T, E>>>::Encoded as FromEncodedStr>::DecodingError:
1350        Debug,
1351    <Ser as Encoder<Result<T, E>>>::Encoded: IntoEncodedString,
1352    <Ser as Decoder<Result<T, E>>>::Encoded: FromEncodedStr,
1353    T: Send + Sync,
1354    E: Send + Sync + Clone,
1355{
1356    /// Applies the given function when a resource that returns `Result<T, E>`
1357    /// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`
1358    /// calls over the `Option<Result<_, _>>` returned by the resource.
1359    ///
1360    /// This is useful when used with features like server functions, in conjunction
1361    /// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are
1362    /// left to handle the `None` and `Err(_)` states.
1363    #[track_caller]
1364    pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {
1365        self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))
1366    }
1367}
1368
1369impl<T, Ser> IntoFuture for Resource<T, Ser>
1370where
1371    T: Clone + Send + Sync + 'static,
1372{
1373    type Output = T;
1374    type IntoFuture = AsyncDerivedFuture<T>;
1375
1376    #[track_caller]
1377    fn into_future(self) -> Self::IntoFuture {
1378        self.data.into_future()
1379    }
1380}
1381
1382impl<T, Ser> Resource<T, Ser>
1383where
1384    T: Send + Sync + 'static,
1385{
1386    /// Returns a new [`Future`] that is ready when the resource has loaded, and accesses its inner
1387    /// value by reference.
1388    pub fn by_ref(&self) -> AsyncDerivedRefFuture<T> {
1389        self.data.by_ref()
1390    }
1391}