Skip to content

Commit 38a3aae

Browse files
authored
Merge branch 'main' into immediate-effect
2 parents e9384e3 + 5eaaff0 commit 38a3aae

File tree

12 files changed

+167
-18
lines changed

12 files changed

+167
-18
lines changed

any_error/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use std::{
88
cell::RefCell,
99
error,
1010
fmt::{self, Display},
11-
future::Future, ops,
11+
future::Future,
12+
ops,
1213
pin::Pin,
1314
sync::Arc,
1415
task::{Context, Poll},
@@ -107,7 +108,9 @@ pub fn get_error_hook() -> Option<Arc<dyn ErrorHook>> {
107108

108109
/// Sets the current thread-local error hook, which will be invoked when [`throw`] is called.
109110
pub fn set_error_hook(hook: Arc<dyn ErrorHook>) -> ResetErrorHookOnDrop {
110-
ResetErrorHookOnDrop(ERROR_HOOK.with_borrow_mut(|this| this.replace(hook)))
111+
ResetErrorHookOnDrop(
112+
ERROR_HOOK.with_borrow_mut(|this| Option::replace(this, hook)),
113+
)
111114
}
112115

113116
/// Invokes the error hook set by [`set_error_hook`] with the given error.

examples/server_fns_axum/Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,20 @@ log = "0.4.22"
2222
simple_logger = "5.0"
2323
serde = { version = "1.0", features = ["derive"] }
2424
axum = { version = "0.7.5", optional = true }
25-
tower = { version = "0.4.13", optional = true }
26-
tower-http = { version = "0.5.2", features = [
25+
tower = { version = "0.5.2", optional = true }
26+
tower-http = { version = "0.6.2", features = [
2727
"fs",
2828
"tracing",
2929
"trace",
3030
], optional = true }
3131
tokio = { version = "1.39", features = ["full"], optional = true }
32-
thiserror = "1.0"
32+
thiserror = "2.0.11"
3333
wasm-bindgen = "0.2.93"
3434
serde_toml = "0.0.1"
3535
toml = "0.8.19"
3636
web-sys = { version = "0.3.70", features = ["FileList", "File"] }
37-
strum = { version = "0.26.3", features = ["strum_macros", "derive"] }
38-
notify = { version = "6.1", optional = true }
37+
strum = { version = "0.27.1", features = ["strum_macros", "derive"] }
38+
notify = { version = "8.0", optional = true }
3939
pin-project-lite = "0.2.14"
4040
dashmap = { version = "6.0", optional = true }
4141
once_cell = { version = "1.19", optional = true }

integrations/axum/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ pub fn generate_request_and_parts(
299299
/// .await
300300
/// .unwrap();
301301
/// }
302+
///
303+
/// # #[cfg(not(feature = "default"))]
304+
/// # fn main() { }
302305
/// ```
303306
/// Leptos provides a generic implementation of `handle_server_fns`. If access to more specific parts of the Request is desired,
304307
/// you can specify your own server fn handler based on this one and give it it's own route in the server macro.
@@ -469,6 +472,9 @@ pub type PinnedHtmlStream =
469472
/// .await
470473
/// .unwrap();
471474
/// }
475+
///
476+
/// # #[cfg(not(feature = "default"))]
477+
/// # fn main() { }
472478
/// ```
473479
///
474480
/// ## Provided Context Types
@@ -555,6 +561,9 @@ where
555561
/// .await
556562
/// .unwrap();
557563
/// }
564+
///
565+
/// # #[cfg(not(feature = "default"))]
566+
/// # fn main() { }
558567
/// ```
559568
///
560569
/// ## Provided Context Types
@@ -961,6 +970,9 @@ fn provide_contexts(
961970
/// .await
962971
/// .unwrap();
963972
/// }
973+
///
974+
/// # #[cfg(not(feature = "default"))]
975+
/// # fn main() { }
964976
/// ```
965977
///
966978
/// ## Provided Context Types

leptos_server/src/local_resource.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ use reactive_graph::{
1212
guards::{AsyncPlain, ReadGuard},
1313
ArcRwSignal, RwSignal,
1414
},
15-
traits::{DefinedAt, IsDisposed, ReadUntracked, Track, Update, Write},
15+
traits::{
16+
DefinedAt, IsDisposed, ReadUntracked, Track, Update, With, Write,
17+
},
1618
};
1719
use send_wrapper::SendWrapper;
1820
use std::{
@@ -91,6 +93,34 @@ impl<T> ArcLocalResource<T> {
9193
pub fn refetch(&self) {
9294
*self.refetch.write() += 1;
9395
}
96+
97+
/// Synchronously, reactively reads the current value of the resource and applies the function
98+
/// `f` to its value if it is `Some(_)`.
99+
#[track_caller]
100+
pub fn map<U>(&self, f: impl FnOnce(&SendWrapper<T>) -> U) -> Option<U>
101+
where
102+
T: 'static,
103+
{
104+
self.data.try_with(|n| n.as_ref().map(f))?
105+
}
106+
}
107+
108+
impl<T, E> ArcLocalResource<Result<T, E>>
109+
where
110+
T: 'static,
111+
E: Clone + 'static,
112+
{
113+
/// Applies the given function when a resource that returns `Result<T, E>`
114+
/// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`
115+
/// calls over the `Option<Result<_, _>>` returned by the resource.
116+
///
117+
/// This is useful when used with features like server functions, in conjunction
118+
/// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are
119+
/// left to handle the `None` and `Err(_)` states.
120+
#[track_caller]
121+
pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {
122+
self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))
123+
}
94124
}
95125

96126
impl<T> IntoFuture for ArcLocalResource<T>

leptos_server/src/once_resource.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,41 @@ where
168168

169169
data
170170
}
171+
172+
/// Synchronously, reactively reads the current value of the resource and applies the function
173+
/// `f` to its value if it is `Some(_)`.
174+
#[track_caller]
175+
pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U>
176+
where
177+
T: Send + Sync + 'static,
178+
{
179+
self.try_with(|n| n.as_ref().map(f))?
180+
}
181+
}
182+
183+
impl<T, E, Ser> ArcOnceResource<Result<T, E>, Ser>
184+
where
185+
Ser: Encoder<Result<T, E>> + Decoder<Result<T, E>>,
186+
<Ser as Encoder<Result<T, E>>>::Error: Debug,
187+
<Ser as Decoder<Result<T, E>>>::Error: Debug,
188+
<<Ser as Decoder<Result<T, E>>>::Encoded as FromEncodedStr>::DecodingError:
189+
Debug,
190+
<Ser as Encoder<Result<T, E>>>::Encoded: IntoEncodedString,
191+
<Ser as Decoder<Result<T, E>>>::Encoded: FromEncodedStr,
192+
T: Send + Sync + 'static,
193+
E: Send + Sync + Clone + 'static,
194+
{
195+
/// Applies the given function when a resource that returns `Result<T, E>`
196+
/// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`
197+
/// calls over the `Option<Result<_, _>>` returned by the resource.
198+
///
199+
/// This is useful when used with features like server functions, in conjunction
200+
/// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are
201+
/// left to handle the `None` and `Err(_)` states.
202+
#[track_caller]
203+
pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {
204+
self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))
205+
}
171206
}
172207

173208
impl<T, Ser> ArcOnceResource<T, Ser> {
@@ -534,6 +569,37 @@ where
534569
defined_at,
535570
}
536571
}
572+
573+
/// Synchronously, reactively reads the current value of the resource and applies the function
574+
/// `f` to its value if it is `Some(_)`.
575+
pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U> {
576+
self.try_with(|n| n.as_ref().map(|n| Some(f(n))))?.flatten()
577+
}
578+
}
579+
580+
impl<T, E, Ser> OnceResource<Result<T, E>, Ser>
581+
where
582+
Ser: Encoder<Result<T, E>> + Decoder<Result<T, E>>,
583+
<Ser as Encoder<Result<T, E>>>::Error: Debug,
584+
<Ser as Decoder<Result<T, E>>>::Error: Debug,
585+
<<Ser as Decoder<Result<T, E>>>::Encoded as FromEncodedStr>::DecodingError:
586+
Debug,
587+
<Ser as Encoder<Result<T, E>>>::Encoded: IntoEncodedString,
588+
<Ser as Decoder<Result<T, E>>>::Encoded: FromEncodedStr,
589+
T: Send + Sync + 'static,
590+
E: Send + Sync + Clone + 'static,
591+
{
592+
/// Applies the given function when a resource that returns `Result<T, E>`
593+
/// has resolved and loaded an `Ok(_)`, rather than requiring nested `.map()`
594+
/// calls over the `Option<Result<_, _>>` returned by the resource.
595+
///
596+
/// This is useful when used with features like server functions, in conjunction
597+
/// with `<ErrorBoundary/>` and `<Suspense/>`, when these other components are
598+
/// left to handle the `None` and `Err(_)` states.
599+
#[track_caller]
600+
pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {
601+
self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))
602+
}
537603
}
538604

539605
impl<T, Ser> OnceResource<T, Ser>

reactive_graph/src/owner.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,9 @@ impl Owner {
207207

208208
/// Runs the given function with this as the current `Owner`.
209209
pub fn with<T>(&self, fun: impl FnOnce() -> T) -> T {
210-
let prev = { OWNER.with(|o| (*o.borrow_mut()).replace(self.clone())) };
210+
let prev = {
211+
OWNER.with(|o| Option::replace(&mut *o.borrow_mut(), self.clone()))
212+
};
211213
#[cfg(feature = "sandboxed-arenas")]
212214
Arena::set(&self.inner.read().or_poisoned().arena);
213215
let val = fun();

reactive_graph/src/transition.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ impl AsyncTransition {
3737
let (tx, rx) = mpsc::channel();
3838
let global_transition = global_transition();
3939
let inner = TransitionInner { tx };
40-
let prev =
41-
(*global_transition.write().or_poisoned()).replace(inner.clone());
40+
let prev = Option::replace(
41+
&mut *global_transition.write().or_poisoned(),
42+
inner.clone(),
43+
);
4244
let value = action().await;
4345
_ = std::mem::replace(
4446
&mut *global_transition.write().or_poisoned(),

reactive_stores/src/arc_field.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ where
3838
track_field: Arc<dyn Fn() + Send + Sync>,
3939
}
4040

41+
impl<T> Debug for ArcField<T>
42+
where
43+
T: 'static,
44+
{
45+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46+
let mut f = f.debug_struct("ArcField");
47+
#[cfg(any(debug_assertions, leptos_debuginfo))]
48+
let f = f.field("defined_at", &self.defined_at);
49+
f.field("path", &self.path)
50+
.field("trigger", &self.trigger)
51+
.finish()
52+
}
53+
}
54+
4155
pub struct StoreFieldReader<T>(Box<dyn Deref<Target = T>>);
4256

4357
impl<T> StoreFieldReader<T> {

reactive_stores/src/field.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ where
3131
inner: ArenaItem<ArcField<T>, S>,
3232
}
3333

34+
impl<T, S> Debug for Field<T, S>
35+
where
36+
T: 'static,
37+
S: Debug,
38+
{
39+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40+
let mut f = f.debug_struct("Field");
41+
#[cfg(any(debug_assertions, leptos_debuginfo))]
42+
let f = f.field("defined_at", &self.defined_at);
43+
f.field("inner", &self.inner).finish()
44+
}
45+
}
46+
3447
impl<T, S> StoreField for Field<T, S>
3548
where
3649
S: Storage<ArcField<T>>,

router/src/location/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ impl Url {
9191
path.push_str(&self.search);
9292
}
9393
if !self.hash.is_empty() {
94-
path.push('#');
94+
if !self.hash.starts_with('#') {
95+
path.push('#');
96+
}
9597
path.push_str(&self.hash);
9698
}
9799
path

0 commit comments

Comments
 (0)