Compare commits

..

1 Commits
v0.7.4 ... 3280

Author SHA1 Message Date
Greg Johnston
c0df839e97 fix: wait for blocking resources before sending subsequent chunks (close #3280) 2024-11-23 09:19:14 -05:00
122 changed files with 957 additions and 2387 deletions

12
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "cargo"
directories:
- "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10

View File

@@ -13,7 +13,6 @@ concurrency:
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
DEBIAN_FRONTEND: noninteractive
jobs:
autofix:
runs-on: ubuntu-latest
@@ -22,10 +21,6 @@ jobs:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
with: {toolchain: nightly, components: "rustfmt, clippy", target: "wasm32-unknown-unknown", rustflags: ""}
- name: Install Glib
run: |
sudo apt-get update
sudo apt-get install -y libglib2.0-dev
- name: Install jq
run: sudo apt-get install jq
- run: |

View File

@@ -8,21 +8,15 @@ on:
branches:
- main
- leptos_0.6
env:
DEBIAN_FRONTEND: noninteractive
jobs:
get-leptos-changed:
uses: ./.github/workflows/get-leptos-changed.yml
test:
needs: [get-leptos-changed]
if: needs.get-leptos-changed.outputs.leptos_changed == 'true' && github.event.pull_request.labels[0].name != 'breaking'
if: github.event.pull_request.labels[0].name == 'semver' # needs.get-leptos-changed.outputs.leptos_changed == 'true' && github.event.pull_request.labels[0].name != 'breaking'
name: Run semver check (nightly-2024-08-01)
runs-on: ubuntu-latest
steps:
- name: Install Glib
run: |
sudo apt-get update
sudo apt-get install -y libglib2.0-dev
- name: Checkout
uses: actions/checkout@v4
- name: Semver Checks

View File

@@ -14,7 +14,6 @@ on:
env:
CARGO_TERM_COLOR: always
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
DEBIAN_FRONTEND: noninteractive
jobs:
test:
name: Run ${{ inputs.cargo_make_task }} (${{ inputs.toolchain }})
@@ -34,10 +33,6 @@ jobs:
echo "Disk space after cleanup:"
df -h
# Setup environment
- name: Install Glib
run: |
sudo apt-get update
sudo apt-get install -y libglib2.0-dev
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@master

635
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -40,36 +40,36 @@ members = [
exclude = ["benchmarks", "examples", "projects"]
[workspace.package]
version = "0.7.4"
version = "0.7.0-rc2"
edition = "2021"
rust-version = "1.76"
[workspace.dependencies]
throw_error = { path = "./any_error/", version = "0.2.0" }
any_spawner = { path = "./any_spawner/", version = "0.2.0" }
throw_error = { path = "./any_error/", version = "0.2.0-rc2" }
any_spawner = { path = "./any_spawner/", version = "0.1.0" }
const_str_slice_concat = { path = "./const_str_slice_concat", version = "0.1.0" }
either_of = { path = "./either_of/", version = "0.1.0" }
hydration_context = { path = "./hydration_context", version = "0.2.0" }
leptos = { path = "./leptos", version = "0.7.4" }
leptos_config = { path = "./leptos_config", version = "0.7.4" }
leptos_dom = { path = "./leptos_dom", version = "0.7.4" }
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.7.4" }
leptos_integration_utils = { path = "./integrations/utils", version = "0.7.4" }
leptos_macro = { path = "./leptos_macro", version = "0.7.4" }
leptos_router = { path = "./router", version = "0.7.4" }
leptos_router_macro = { path = "./router_macro", version = "0.7.4" }
leptos_server = { path = "./leptos_server", version = "0.7.4" }
leptos_meta = { path = "./meta", version = "0.7.4" }
next_tuple = { path = "./next_tuple", version = "0.1.0" }
hydration_context = { path = "./hydration_context", version = "0.2.0-rc2" }
leptos = { path = "./leptos", version = "0.7.0-rc2" }
leptos_config = { path = "./leptos_config", version = "0.7.0-rc2" }
leptos_dom = { path = "./leptos_dom", version = "0.7.0-rc2" }
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.7.0-rc2" }
leptos_integration_utils = { path = "./integrations/utils", version = "0.7.0-rc2" }
leptos_macro = { path = "./leptos_macro", version = "0.7.0-rc2" }
leptos_router = { path = "./router", version = "0.7.0-rc2" }
leptos_router_macro = { path = "./router_macro", version = "0.7.0-rc2" }
leptos_server = { path = "./leptos_server", version = "0.7.0-rc2" }
leptos_meta = { path = "./meta", version = "0.7.0-rc2" }
next_tuple = { path = "./next_tuple", version = "0.1.0-rc2" }
oco_ref = { path = "./oco", version = "0.2.0" }
or_poisoned = { path = "./or_poisoned", version = "0.1.0" }
reactive_graph = { path = "./reactive_graph", version = "0.1.0" }
reactive_stores = { path = "./reactive_stores", version = "0.1.0" }
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.1.0" }
server_fn = { path = "./server_fn", version = "0.7.4" }
server_fn_macro = { path = "./server_fn_macro", version = "0.7.4" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.7.4" }
tachys = { path = "./tachys", version = "0.1.0" }
reactive_graph = { path = "./reactive_graph", version = "0.1.0-rc2" }
reactive_stores = { path = "./reactive_stores", version = "0.1.0-rc2" }
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.1.0-rc2" }
server_fn = { path = "./server_fn", version = "0.7.0-rc2" }
server_fn_macro = { path = "./server_fn_macro", version = "0.7.0-rc2" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.7.0-rc2" }
tachys = { path = "./tachys", version = "0.1.0-rc2" }
[profile.release]
codegen-units = 1
@@ -78,9 +78,3 @@ opt-level = 'z'
[workspace.metadata.cargo-all-features]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
[workspace.lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = [
'cfg(leptos_debuginfo)',
'cfg(erase_components)',
] }

View File

@@ -12,6 +12,8 @@
You can find a list of useful libraries and example projects at [`awesome-leptos`](https://github.com/leptos-rs/awesome-leptos).
# The `main` branch is currently undergoing major changes in preparation for the [0.7](https://github.com/leptos-rs/leptos/milestone/4) release. For a stable version, please use the [v0.6.13 tag](https://github.com/leptos-rs/leptos/tree/v0.6.13)
# Leptos
```rust

View File

@@ -1,6 +1,6 @@
[package]
name = "throw_error"
version = "0.2.0"
version = "0.2.0-rc2"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -1,6 +1,6 @@
[package]
name = "any_spawner"
version = "0.2.1"
version = "0.1.1"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"
@@ -16,8 +16,8 @@ thiserror = "2.0"
tokio = { version = "1.41", optional = true, default-features = false, features = [
"rt",
] }
tracing = { version = "0.1.41", optional = true }
wasm-bindgen-futures = { version = "0.4.47", optional = true }
tracing = { version = "0.1.40", optional = true }
wasm-bindgen-futures = { version = "0.4.45", optional = true }
[features]
async-executor = ["dep:async-executor"]

View File

@@ -1,6 +1,6 @@
[package]
name = "either_of"
version = "0.1.3"
version = "0.1.0"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -134,7 +134,7 @@ tuples!(EitherOf14 + EitherOf14Future + EitherOf14FutureProj => A, B, C, D, E, F
tuples!(EitherOf15 + EitherOf15Future + EitherOf15FutureProj => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
tuples!(EitherOf16 + EitherOf16Future + EitherOf16FutureProj => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
/// Matches over the first expression and returns an either ([`Either`], [`EitherOf3`], ... [`EitherOf8`])
/// Matches over the first expression and returns an either ([`Either`], [`EitherOf3`], ... [`EitherOf6`])
/// composed of the values returned by the match arms.
///
/// The pattern syntax is exactly the same as found in a match arm.
@@ -197,29 +197,6 @@ macro_rules! either {
$e_pattern => $crate::EitherOf6::E($e_expression),
$f_pattern => $crate::EitherOf6::F($f_expression),
}
};
($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr,) => {
match $match {
$a_pattern => $crate::EitherOf7::A($a_expression),
$b_pattern => $crate::EitherOf7::B($b_expression),
$c_pattern => $crate::EitherOf7::C($c_expression),
$d_pattern => $crate::EitherOf7::D($d_expression),
$e_pattern => $crate::EitherOf7::E($e_expression),
$f_pattern => $crate::EitherOf7::F($f_expression),
$g_pattern => $crate::EitherOf7::G($g_expression),
}
};
($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr, $d_pattern:pat => $d_expression:expr, $e_pattern:pat => $e_expression:expr, $f_pattern:pat => $f_expression:expr, $g_pattern:pat => $g_expression:expr, $h_pattern:pat => $h_expression:expr,) => {
match $match {
$a_pattern => $crate::EitherOf8::A($a_expression),
$b_pattern => $crate::EitherOf8::B($b_expression),
$c_pattern => $crate::EitherOf8::C($c_expression),
$d_pattern => $crate::EitherOf8::D($d_expression),
$e_pattern => $crate::EitherOf8::E($e_expression),
$f_pattern => $crate::EitherOf8::F($f_expression),
$g_pattern => $crate::EitherOf8::G($g_expression),
$h_pattern => $crate::EitherOf8::H($h_expression),
}
}; // if you need more eithers feel free to open a PR ;-)
}
@@ -256,23 +233,4 @@ fn either_macro() {
16 => 24u8,
_ => 12,
);
let _: EitherOf7<&str, f64, char, f32, u8, i8, i32> = either!(12,
12 => "12",
13 => 0.0,
14 => ' ',
15 => 0.0f32,
16 => 24u8,
17 => 2i8,
_ => 12,
);
let _: EitherOf8<&str, f64, char, f32, u8, i8, u32, i32> = either!(12,
12 => "12",
13 => 0.0,
14 => ' ',
15 => 0.0f32,
16 => 24u8,
17 => 2i8,
18 => 42u32,
_ => 12,
);
}

View File

@@ -1,6 +1,6 @@
[package]
name = "hydration_context"
version = "0.2.1"
version = "0.2.0-rc2"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"
@@ -14,8 +14,8 @@ throw_error = { workspace = true }
or_poisoned = { workspace = true }
futures = "0.3.31"
serde = { version = "1.0", features = ["derive"] }
wasm-bindgen = { version = "0.2.97", optional = true }
js-sys = { version = "0.3.74", optional = true }
wasm-bindgen = { version = "0.2.95", optional = true }
js-sys = { version = "0.3.72", optional = true }
once_cell = "1.20"
pin-project-lite = "0.2.15"
@@ -25,6 +25,3 @@ browser = ["dep:wasm-bindgen", "dep:js-sys"]
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(leptos_debuginfo)'] }

View File

@@ -1,8 +1,3 @@
// #[wasm_bindgen(thread_local)] is deprecated in wasm-bindgen 0.2.96
// but the replacement is also only shipped in that version
// as a result, we'll just allow deprecated for now
#![allow(deprecated)]
use super::{SerializedDataId, SharedContext};
use crate::{PinnedFuture, PinnedStream};
use core::fmt::Debug;

View File

@@ -18,7 +18,7 @@ hydration_context = { workspace = true }
leptos = { workspace = true, features = ["nonce", "ssr"] }
leptos_integration_utils = { workspace = true }
leptos_macro = { workspace = true, features = ["actix"] }
leptos_meta = { workspace = true, features = ["nonce"] }
leptos_meta = { workspace = true }
leptos_router = { workspace = true, features = ["ssr"] }
server_fn = { workspace = true, features = ["actix"] }
serde_json = "1.0"

View File

@@ -375,8 +375,8 @@ pub fn handle_server_fns_with_context(
.take(),
);
// if it accepts text/html (i.e., is a plain form post) and doesn't already have a
// Location set, then redirect to the Referer
// it it accepts text/html (i.e., is a plain form post) and doesn't already have a
// Location set, then redirect to to Referer
if accepts_html {
if let Some(referrer) = referrer {
let has_location =
@@ -390,20 +390,7 @@ pub fn handle_server_fns_with_context(
}
}
// the Location header may have been set to Referer, so any redirection by the
// user must overwrite it
{
let mut res_options = res_options.0.write();
let headers = res.0.headers_mut();
for location in
res_options.headers.remove(header::LOCATION)
{
headers.insert(header::LOCATION, location);
}
}
// apply status code and headers if user changed them
// apply status code and headers if used changed them
res.extend_response(&res_options);
res.0
})

View File

@@ -19,7 +19,7 @@ futures = "0.3.31"
leptos = { workspace = true, features = ["nonce", "ssr"] }
server_fn = { workspace = true, features = ["axum-no-default"] }
leptos_macro = { workspace = true, features = ["axum"] }
leptos_meta = { workspace = true, features = ["ssr", "nonce"] }
leptos_meta = { workspace = true, features = ["ssr"] }
leptos_router = { workspace = true, features = ["ssr"] }
leptos_integration_utils = { workspace = true }
once_cell = "1"
@@ -27,7 +27,7 @@ parking_lot = "0.12.3"
tokio = { version = "1.41", default-features = false }
tower = { version = "0.5.1", features = ["util"] }
tower-http = "0.6.2"
tracing = { version = "0.1.41", optional = true }
tracing = { version = "0.1.40", optional = true }
[dev-dependencies]
axum = "0.7.9"

View File

@@ -16,6 +16,7 @@
//! - `default`: supports running in a typical native Tokio/Axum environment
//! - `wasm`: with `default-features = false`, supports running in a JS Fetch-based
//! environment
//! - `islands`: activates Leptos [islands mode](https://leptos-rs.github.io/leptos/islands.html)
//!
//! ### Important Note
//! Prior to 0.5, using `default-features = false` on `leptos_axum` simply did nothing. Now, it actively
@@ -398,8 +399,8 @@ async fn handle_server_fns_inner(
// actually run the server fn
let mut res = AxumResponse(service.run(req).await);
// if it accepts text/html (i.e., is a plain form post) and doesn't already have a
// Location set, then redirect to the Referer
// it it accepts text/html (i.e., is a plain form post) and doesn't already have a
// Location set, then redirect to to Referer
if accepts_html {
if let Some(referrer) = referrer {
let has_location =
@@ -411,7 +412,7 @@ async fn handle_server_fns_inner(
}
}
// apply status code and headers if user changed them
// apply status code and headers if used changed them
res.extend_response(&res_options);
Ok(res.0)
})
@@ -1993,12 +1994,12 @@ pub fn file_and_error_handler<S, IV>(
+ 'static
where
IV: IntoView + 'static,
S: Send + Sync + Clone + 'static,
S: Send + 'static,
LeptosOptions: FromRef<S>,
{
move |uri: Uri, State(state): State<S>, req: Request<Body>| {
move |uri: Uri, State(options): State<S>, req: Request<Body>| {
Box::pin(async move {
let options = LeptosOptions::from_ref(&state);
let options = LeptosOptions::from_ref(&options);
let res = get_static_file(uri, &options.site_root, req.headers());
let res = res.await.unwrap();
@@ -2006,9 +2007,7 @@ where
res.into_response()
} else {
let mut res = handle_response_inner(
move || {
provide_context(state.clone());
},
|| {},
move || shell(options),
req,
|app, chunks| {

View File

@@ -11,10 +11,7 @@ edition.workspace = true
[dependencies]
throw_error = { workspace = true }
any_spawner = { workspace = true, features = [
"wasm-bindgen",
"futures-executor",
] }
any_spawner = { workspace = true, features = ["wasm-bindgen", "futures-executor"] }
base64 = { version = "0.22.1", optional = true }
cfg-if = "1.0"
hydration_context = { workspace = true }
@@ -31,13 +28,9 @@ paste = "1.0"
rand = { version = "0.8.5", optional = true }
reactive_graph = { workspace = true, features = ["serde"] }
rustc-hash = "2.0"
tachys = { workspace = true, features = [
"reactive_graph",
"reactive_stores",
"oco",
] }
tachys = { workspace = true, features = ["reactive_graph", "reactive_stores", "oco"] }
thiserror = "2.0"
tracing = { version = "0.1.41", optional = true }
tracing = { version = "0.1.40", optional = true }
typed-builder = "0.20.0"
typed-builder-macro = "0.20.0"
serde = "1.0"
@@ -52,27 +45,25 @@ web-sys = { version = "0.3.72", features = [
"ShadowRootInit",
"ShadowRootMode",
] }
wasm-bindgen = "0.2.97"
wasm-bindgen = "0.2.95"
serde_qs = "0.13.0"
slotmap = "1.0"
futures = "0.3.31"
send_wrapper = "0.6.0"
getrandom = { version = "0.2", features = ["js"], optional = true }
[features]
hydration = [
"reactive_graph/hydration",
"leptos_server/hydration",
"hydration_context/browser",
"leptos_dom/hydration",
"leptos_dom/hydration"
]
csr = ["leptos_macro/csr", "reactive_graph/effects", "dep:getrandom"]
csr = ["leptos_macro/csr", "reactive_graph/effects"]
hydrate = [
"leptos_macro/hydrate",
"hydration",
"tachys/hydrate",
"reactive_graph/effects",
"dep:getrandom",
]
default-tls = ["server_fn/default-tls"]
rustls = ["server_fn/rustls"]
@@ -84,10 +75,7 @@ ssr = [
"tachys/ssr",
]
nightly = ["leptos_macro/nightly", "reactive_graph/nightly", "tachys/nightly"]
rkyv = [
"server_fn/rkyv",
"leptos_server/rkyv"
]
rkyv = ["server_fn/rkyv"]
tracing = [
"dep:tracing",
"reactive_graph/tracing",
@@ -101,7 +89,7 @@ spin = ["leptos-spin-macro"]
islands = ["leptos_macro/islands", "dep:serde_json"]
trace-component-props = [
"leptos_macro/trace-component-props",
"leptos_dom/trace-component-props",
"leptos_dom/trace-component-props"
]
delegation = ["tachys/delegation"]
@@ -113,56 +101,23 @@ denylist = [
"rustls",
"default-tls",
"wasm-bindgen",
"rkyv", # was causing clippy issues on nightly
"rkyv", # was causing clippy issues on nightly
"trace-component-props",
"spin",
"islands",
]
skip_feature_sets = [
[
"csr",
"ssr",
],
[
"csr",
"hydrate",
],
[
"ssr",
"hydrate",
],
[
"serde",
"serde-lite",
],
[
"serde-lite",
"miniserde",
],
[
"serde",
"miniserde",
],
[
"serde",
"rkyv",
],
[
"miniserde",
"rkyv",
],
[
"serde-lite",
"rkyv",
],
[
"default-tls",
"rustls",
],
["csr", "ssr"],
["csr", "hydrate"],
["ssr", "hydrate"],
["serde", "serde-lite"],
["serde-lite", "miniserde"],
["serde", "miniserde"],
["serde", "rkyv"],
["miniserde", "rkyv"],
["serde-lite", "rkyv"],
["default-tls", "rustls"],
]
[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(leptos_debuginfo)'] }

View File

@@ -1,150 +0,0 @@
use crate::attr::{
any_attribute::{AnyAttribute, IntoAnyAttribute},
Attribute, NextAttribute,
};
use leptos::prelude::*;
/// Function stored to build/rebuild the wrapped children when attributes are added.
type ChildBuilder<T> = dyn Fn(AnyAttribute) -> T + Send + Sync + 'static;
/// Intercepts attributes passed to your component, allowing passing them to any element.
///
/// By default, Leptos passes any attributes passed to your component (e.g. `<MyComponent
/// attr:class="some-class"/>`) to the top-level element in the view returned by your component.
/// [`AttributeInterceptor`] allows you to intercept this behavior and pass it onto any element in
/// your component instead.
///
/// Must be the top level element in your component's view.
///
/// ## Example
///
/// Any attributes passed to MyComponent will be passed to the #inner element.
///
/// ```
/// # use leptos::prelude::*;
/// use leptos::attribute_interceptor::AttributeInterceptor;
///
/// #[component]
/// pub fn MyComponent() -> impl IntoView {
/// view! {
/// <AttributeInterceptor let:attrs>
/// <div id="wrapper">
/// <div id="inner" {..attrs} />
/// </div>
/// </AttributeInterceptor>
/// }
/// }
/// ```
#[component(transparent)]
pub fn AttributeInterceptor<Chil, T>(
/// The elements that will be rendered, with the attributes this component received as a
/// parameter.
children: Chil,
) -> impl IntoView
where
Chil: Fn(AnyAttribute) -> T + Send + Sync + 'static,
T: IntoView,
{
AttributeInterceptorInner::new(children)
}
/// Wrapper to intercept attributes passed to a component so you can apply them to a different
/// element.
struct AttributeInterceptorInner<T: IntoView, A> {
children_builder: Box<ChildBuilder<T>>,
children: T,
attributes: A,
}
impl<T: IntoView> AttributeInterceptorInner<T, ()> {
/// Use this as the returned view from your component to collect the attributes that are passed
/// to your component so you can manually handle them.
pub fn new<F>(children: F) -> Self
where
F: Fn(AnyAttribute) -> T + Send + Sync + 'static,
{
let children_builder = Box::new(children);
let children = children_builder(().into_any_attr());
Self {
children_builder,
children,
attributes: (),
}
}
}
impl<T: IntoView, A: Attribute> Render for AttributeInterceptorInner<T, A> {
type State = <T as Render>::State;
fn build(self) -> Self::State {
self.children.build()
}
fn rebuild(self, state: &mut Self::State) {
self.children.rebuild(state);
}
}
impl<T: IntoView, A> AddAnyAttr for AttributeInterceptorInner<T, A>
where
A: Attribute,
{
type Output<SomeNewAttr: leptos::attr::Attribute> =
AttributeInterceptorInner<T, <<A as NextAttribute>::Output<SomeNewAttr> as Attribute>::CloneableOwned>;
fn add_any_attr<NewAttr: leptos::attr::Attribute>(
self,
attr: NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml,
{
let attributes =
self.attributes.add_any_attr(attr).into_cloneable_owned();
let children =
(self.children_builder)(attributes.clone().into_any_attr());
AttributeInterceptorInner {
children_builder: self.children_builder,
children,
attributes,
}
}
}
impl<T: IntoView, A: Attribute> RenderHtml for AttributeInterceptorInner<T, A> {
type AsyncOutput = T::AsyncOutput;
const MIN_LENGTH: usize = T::MIN_LENGTH;
fn dry_resolve(&mut self) {
self.children.dry_resolve()
}
fn resolve(
self,
) -> impl std::future::Future<Output = Self::AsyncOutput> + Send {
self.children.resolve()
}
fn to_html_with_buf(
self,
buf: &mut String,
position: &mut leptos::tachys::view::Position,
escape: bool,
mark_branches: bool,
) {
self.children
.to_html_with_buf(buf, position, escape, mark_branches)
}
fn hydrate<const FROM_SERVER: bool>(
self,
cursor: &leptos::tachys::hydration::Cursor,
position: &leptos::tachys::view::PositionState,
) -> Self::State {
self.children.hydrate::<FROM_SERVER>(cursor, position)
}
}

View File

@@ -85,7 +85,7 @@ type BoxedChildrenFn = Box<dyn Fn() -> AnyView + Send>;
/// )
/// }
pub trait ToChildren<F> {
/// Convert the provided type (generally a closure) to Self (generally a "children" type,
/// Convert the provided type to (generally a closure) to Self (generally a "children" type,
/// e.g., [Children]). See the implementations to see exactly which input types are supported
/// and which "children" type they are converted to.
fn to_children(f: F) -> Self;
@@ -285,13 +285,6 @@ impl<T> Debug for TypedChildrenFn<T> {
}
}
impl<T> Clone for TypedChildrenFn<T> {
// Manual implementation to avoid the `T: Clone` bound.
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T> TypedChildrenFn<T> {
/// Extracts the inner `children` function.
pub fn into_inner(self) -> Arc<dyn Fn() -> View<T> + Send + Sync> {

View File

@@ -44,67 +44,6 @@ use tachys::{reactive_graph::OwnedView, view::keyed::keyed};
/// }
/// }
/// ```
///
/// For convenience, you can also choose to write template code directly in the `<For>`
/// component, using the `let` syntax:
///
/// ```
/// # use leptos::prelude::*;
///
/// # #[derive(Copy, Clone, Debug, PartialEq, Eq)]
/// # struct Counter {
/// # id: usize,
/// # count: RwSignal<i32>
/// # }
/// #
/// # #[component]
/// # fn Counters() -> impl IntoView {
/// # let (counters, set_counters) = create_signal::<Vec<Counter>>(vec![]);
/// #
/// view! {
/// <div>
/// <For
/// each=move || counters.get()
/// key=|counter| counter.id
/// let(counter)
/// >
/// <button>"Value: " {move || counter.count.get()}</button>
/// </For>
/// </div>
/// }
/// # }
/// ```
///
/// The `let` syntax also supports destructuring the pattern of your data.
/// `let((one, two))` in the case of tuples, and `let(Struct { field_one, field_two })`
/// in the case of structs.
///
/// ```
/// # use leptos::prelude::*;
///
/// # #[derive(Copy, Clone, Debug, PartialEq, Eq)]
/// # struct Counter {
/// # id: usize,
/// # count: RwSignal<i32>
/// # }
/// #
/// # #[component]
/// # fn Counters() -> impl IntoView {
/// # let (counters, set_counters) = create_signal::<Vec<Counter>>(vec![]);
/// #
/// view! {
/// <div>
/// <For
/// each=move || counters.get()
/// key=|counter| counter.id
/// let(Counter { id, count })
/// >
/// <button>"Value: " {move || count.get()}</button>
/// </For>
/// </div>
/// }
/// # }
/// ```
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
#[component]
pub fn For<IF, I, T, EF, N, KF, K>(

View File

@@ -1,6 +1,4 @@
((root, pkg_path, output_name, wasm_output_name) => {
let MOST_RECENT_CHILDREN_CB;
function idle(c) {
if ("requestIdleCallback" in window) {
window.requestIdleCallback(c);
@@ -8,49 +6,55 @@
c();
}
}
function hydrateIslands(rootNode, mod) {
function traverse(node) {
function islandTree(rootNode) {
const tree = [];
function traverse(node, parent) {
if (node.nodeType === Node.ELEMENT_NODE) {
const tag = node.tagName.toLowerCase();
if(tag === 'leptos-island') {
if(node.tagName.toLowerCase() === 'leptos-island') {
const children = [];
const id = node.dataset.component || null;
hydrateIsland(node, id, mod);
const data = { id, node, children };
for(const child of node.children) {
traverse(child, children);
}
(parent || tree).push(data);
} else {
if(tag === 'leptos-children') {
MOST_RECENT_CHILDREN_CB = node.$$on_hydrate;
}
for(const child of node.children) {
traverse(child);
traverse(child, parent);
};
}
}
}
traverse(rootNode);
traverse(rootNode, null);
return { el: null, id: null, children: tree };
}
function hydrateIsland(el, id, mod) {
const islandFn = mod[id];
if (islandFn) {
if (MOST_RECENT_CHILDREN_CB) {
MOST_RECENT_CHILDREN_CB();
}
islandFn(el);
} else {
console.warn(`Could not find WASM function for the island ${id}.`);
}
}
function hydrateIslands(entry, mod) {
if(entry.node) {
hydrateIsland(entry.node, entry.id, mod);
}
for (const island of entry.children) {
hydrateIslands(island, mod);
}
}
idle(() => {
import(`${root}/${pkg_path}/${output_name}.js`)
.then(mod => {
mod.default(`${root}/${pkg_path}/${wasm_output_name}.wasm`).then(() => {
mod.hydrate();
hydrateIslands(document.body, mod);
hydrateIslands(islandTree(document.body, null), mod);
});
})
});

View File

@@ -192,9 +192,6 @@ pub mod callback;
/// Types that can be passed as the `children` prop of a component.
pub mod children;
/// Wrapper for intercepting component attributes.
pub mod attribute_interceptor;
#[doc(hidden)]
/// Traits used to implement component constructors.
pub mod component;
@@ -293,7 +290,7 @@ pub mod logging {
/// Utilities for working with asynchronous tasks.
pub mod task {
pub use any_spawner::{self, CustomExecutor, Executor};
pub use any_spawner::Executor;
use std::future::Future;
/// Spawns a thread-safe [`Future`].

View File

@@ -51,13 +51,6 @@ use tachys::html::attribute::AttributeValue;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Nonce(pub(crate) Arc<str>);
impl Nonce {
/// Returns a reference to the inner reference-counted string slice representing the nonce.
pub fn as_inner(&self) -> &Arc<str> {
&self.0
}
}
impl Deref for Nonce {
type Target = str;

View File

@@ -16,7 +16,6 @@ use reactive_graph::{
traits::{Dispose, Get, Read, Track, With},
};
use slotmap::{DefaultKey, SlotMap};
use std::sync::Arc;
use tachys::{
either::Either,
html::attribute::Attribute,
@@ -133,18 +132,6 @@ where
})
}
fn nonce_or_not() -> Option<Arc<str>> {
#[cfg(feature = "nonce")]
{
use crate::nonce::Nonce;
use_context::<Nonce>().map(|n| n.0)
}
#[cfg(not(feature = "nonce"))]
{
None
}
}
pub(crate) struct SuspenseBoundary<const TRANSITION: bool, Fal, Chil> {
pub id: SerializedDataId,
pub none_pending: ArcMemo<bool>,
@@ -392,12 +379,7 @@ where
&mut fallback_position,
mark_branches,
);
buf.push_async_out_of_order_with_nonce(
fut,
position,
mark_branches,
nonce_or_not(),
);
buf.push_async_out_of_order(fut, position, mark_branches);
} else {
buf.push_async({
let mut position = *position;

View File

@@ -85,44 +85,41 @@ pub fn Transition<Chil>(
where
Chil: IntoView + Send + 'static,
{
let owner = Owner::new();
owner.with(|| {
let (starts_local, id) = {
Owner::current_shared_context()
.map(|sc| {
let id = sc.next_id();
(sc.get_incomplete_chunk(&id), id)
})
.unwrap_or_else(|| (false, Default::default()))
};
let fallback = fallback.run();
let children = children.into_inner()();
let tasks = ArcRwSignal::new(SlotMap::<DefaultKey, ()>::new());
provide_context(SuspenseContext {
tasks: tasks.clone(),
});
let none_pending = ArcMemo::new(move |prev: Option<&bool>| {
tasks.track();
if prev.is_none() && starts_local {
false
} else {
tasks.with(SlotMap::is_empty)
let (starts_local, id) = {
Owner::current_shared_context()
.map(|sc| {
let id = sc.next_id();
(sc.get_incomplete_chunk(&id), id)
})
.unwrap_or_else(|| (false, Default::default()))
};
let fallback = fallback.run();
let children = children.into_inner()();
let tasks = ArcRwSignal::new(SlotMap::<DefaultKey, ()>::new());
provide_context(SuspenseContext {
tasks: tasks.clone(),
});
let none_pending = ArcMemo::new(move |prev: Option<&bool>| {
tasks.track();
if prev.is_none() && starts_local {
false
} else {
tasks.with(SlotMap::is_empty)
}
});
if let Some(set_pending) = set_pending {
Effect::new_isomorphic({
let none_pending = none_pending.clone();
move |_| {
set_pending.set(!none_pending.get());
}
});
if let Some(set_pending) = set_pending {
Effect::new_isomorphic({
let none_pending = none_pending.clone();
move |_| {
set_pending.set(!none_pending.get());
}
});
}
}
OwnedView::new(SuspenseBoundary::<true, _, _> {
id,
none_pending,
fallback,
children,
})
OwnedView::new(SuspenseBoundary::<true, _, _> {
id,
none_pending,
fallback,
children,
})
}

View File

@@ -1535,7 +1535,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
@@ -1851,7 +1851,7 @@ dependencies = [
[[package]]
name = "quote-use"
version = "0.7.2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58e9a38ef862d7fec635661503289062bc5b3035e61859a8de3d3f81823accd2"
dependencies = [
@@ -1953,7 +1953,7 @@ dependencies = [
[[package]]
name = "ron"
version = "0.7.2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a"
dependencies = [
@@ -2104,7 +2104,7 @@ dependencies = [
[[package]]
name = "serde_urlencoded"
version = "0.7.2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [

View File

@@ -26,6 +26,3 @@ temp-env = { version = "0.3.6", features = ["async_closure"] }
[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(leptos_debuginfo)'] }

View File

@@ -12,10 +12,10 @@ edition.workspace = true
tachys = { workspace = true }
reactive_graph = { workspace = true }
or_poisoned = { workspace = true }
js-sys = "0.3.74"
js-sys = "0.3.72"
send_wrapper = "0.6.0"
tracing = { version = "0.1.41", optional = true }
wasm-bindgen = "0.2.97"
tracing = { version = "0.1.40", optional = true }
wasm-bindgen = "0.2.95"
serde_json = { version = "1.0", optional = true }
serde = { version = "1.0", optional = true }
@@ -37,6 +37,3 @@ rustdoc-args = ["--generate-link-to-definition"]
[package.metadata.cargo-all-features]
denylist = ["tracing"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(leptos_debuginfo)'] }

View File

@@ -131,10 +131,6 @@ impl AnimationFrameRequestHandle {
/// Runs the given function between the next repaint using
/// [`Window.requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).
///
/// ### Note about Context
///
/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).
#[cfg_attr(feature = "tracing", instrument(level = "trace", skip_all))]
#[inline(always)]
pub fn request_animation_frame(cb: impl FnOnce() + 'static) {
@@ -163,10 +159,6 @@ fn closure_once(cb: impl FnOnce() + 'static) -> JsValue {
/// Runs the given function between the next repaint using
/// [`Window.requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame),
/// returning a cancelable handle.
///
/// ### Note about Context
///
/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).
#[cfg_attr(feature = "tracing", instrument(level = "trace", skip_all))]
#[inline(always)]
pub fn request_animation_frame_with_handle(
@@ -205,10 +197,6 @@ impl IdleCallbackHandle {
/// Queues the given function during an idle period using
/// [`Window.requestIdleCallback`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestIdleCallback).
///
/// ### Note about Context
///
/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).
#[cfg_attr(feature = "tracing", instrument(level = "trace", skip_all))]
#[inline(always)]
pub fn request_idle_callback(cb: impl Fn() + 'static) {
@@ -218,10 +206,6 @@ pub fn request_idle_callback(cb: impl Fn() + 'static) {
/// Queues the given function during an idle period using
/// [`Window.requestIdleCallback`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestIdleCallback),
/// returning a cancelable handle.
///
/// ### Note about Context
///
/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).
#[cfg_attr(feature = "tracing", instrument(level = "trace", skip_all))]
#[inline(always)]
pub fn request_idle_callback_with_handle(
@@ -255,8 +239,6 @@ pub fn request_idle_callback_with_handle(
/// to perform final cleanup or other just-before-rendering tasks.
///
/// [MDN queueMicrotask](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)
///
/// <div class="warning">The task is called outside of the ownership tree, this means that if you want to access for example the context you need to reestablish the owner.</div>
pub fn queue_microtask(task: impl FnOnce() + 'static) {
use js_sys::{Function, Reflect};
@@ -283,10 +265,6 @@ impl TimeoutHandle {
/// Executes the given function after the given duration of time has passed.
/// [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout).
///
/// ### Note about Context
///
/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).
#[cfg_attr(
feature = "tracing",
instrument(level = "trace", skip_all, fields(duration = ?duration))
@@ -297,10 +275,6 @@ pub fn set_timeout(cb: impl FnOnce() + 'static, duration: Duration) {
/// Executes the given function after the given duration of time has passed, returning a cancelable handle.
/// [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout).
///
/// ### Note about Context
///
/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).
#[cfg_attr(
feature = "tracing",
instrument(level = "trace", skip_all, fields(duration = ?duration))
@@ -357,10 +331,6 @@ pub fn set_timeout_with_handle(
/// }
/// }
/// ```
///
/// ### Note about Context
///
/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).
pub fn debounce<T: 'static>(
delay: Duration,
mut cb: impl FnMut(T) + 'static,
@@ -428,10 +398,6 @@ impl IntervalHandle {
/// Repeatedly calls the given function, with a delay of the given duration between calls.
/// See [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval).
///
/// ### Note about Context
///
/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).
#[cfg_attr(
feature = "tracing",
instrument(level = "trace", skip_all, fields(duration = ?duration))
@@ -443,10 +409,6 @@ pub fn set_interval(cb: impl Fn() + 'static, duration: Duration) {
/// Repeatedly calls the given function, with a delay of the given duration between calls,
/// returning a cancelable handle.
/// See [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval).
///
/// ### Note about Context
///
/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).
#[cfg_attr(
feature = "tracing",
instrument(level = "trace", skip_all, fields(duration = ?duration))
@@ -489,10 +451,6 @@ pub fn set_interval_with_handle(
/// Adds an event listener to the `Window`, typed as a generic `Event`,
/// returning a cancelable handle.
///
/// ### Note about Context
///
/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).
#[cfg_attr(
feature = "tracing",
instrument(level = "trace", skip_all, fields(event_name = %event_name))
@@ -561,10 +519,6 @@ pub fn window_event_listener_untyped(
/// on_cleanup(move || handle.remove());
/// }
/// ```
///
/// ### Note about Context
///
/// The callback is called outside of the reactive ownership tree. This means that it does not have access to context via [`use_context`](reactive_graph::owner::use_context). If you want to use context inside the callback, you should either call `use_context` in the body of the component, and move the value into the callback, or access the current owner inside the component body using [`Owner::current`](reactive_graph::owner::Owner::current) and reestablish it in the callback with [`Owner::with`](reactive_graph::owner::Owner::with).
pub fn window_event_listener<E: EventDescriptor + 'static>(
event: E,
cb: impl Fn(E::EventType) + 'static,

View File

@@ -29,9 +29,14 @@ macro_rules! error {
macro_rules! debug_warn {
($($x:tt)*) => {
{
if cfg!(debug_assertions) {
#[cfg(debug_assertions)]
{
$crate::warn!($($x)*)
}
#[cfg(not(debug_assertions))]
{
($($x)*)
}
}
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos_macro"
version = "0.7.4"
version = "0.7.0-rc2"
authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
@@ -27,7 +27,7 @@ leptos_hot_reload = { workspace = true }
server_fn_macro = { workspace = true }
convert_case = "0.6.0"
uuid = { version = "1.11", features = ["v4"] }
tracing = { version = "0.1.41", optional = true }
tracing = { version = "0.1.40", optional = true }
[dev-dependencies]
log = "0.4.22"
@@ -45,7 +45,6 @@ ssr = ["server_fn_macro/ssr", "leptos/ssr"]
nightly = ["server_fn_macro/nightly"]
tracing = ["dep:tracing"]
islands = []
trace-components = []
trace-component-props = []
actix = ["server_fn_macro/actix"]
axum = ["server_fn_macro/axum"]
@@ -84,7 +83,4 @@ skip_feature_sets = [
rustdoc-args = ["--generate-link-to-definition"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = [
'cfg(leptos_debuginfo)',
'cfg(erase_components)',
] }
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(erase_components)'] }

View File

@@ -201,35 +201,13 @@ impl ToTokens for Model {
) = {
#[cfg(feature = "tracing")]
{
/* TODO for 0.8: fix this
*
* The problem is that cargo now warns about an expected "tracing" cfg if
* you don't have a "tracing" feature in your actual crate
*
* However, until https://github.com/tokio-rs/tracing/pull/1819 is merged
* (?), you can't provide an alternate path for `tracing` (for example,
* ::leptos::tracing), which means that if you're going to use the macro
* you *must* have `tracing` in your Cargo.toml.
*
* Including the feature-check here causes cargo warnings on
* previously-working projects.
*
* Removing the feature-check here breaks any project that uses leptos with
* the tracing feature turned on, but without a tracing dependency in its
* Cargo.toml.
* /
*/
let instrument = cfg!(feature = "trace-components").then(|| quote! {
#[cfg_attr(
feature = "tracing",
::leptos::tracing::instrument(level = "info", name = #trace_name, skip_all)
)]
});
(
quote! {
#[allow(clippy::let_with_type_underscore)]
#instrument
#[cfg_attr(
feature = "tracing",
::leptos::tracing::instrument(level = "info", name = #trace_name, skip_all)
)]
},
quote! {
let __span = ::leptos::tracing::Span::current();
@@ -282,12 +260,8 @@ impl ToTokens for Model {
let body_name = unmodified_fn_name_from_fn_name(&body_name);
let body_expr = if is_island {
quote! {
::leptos::reactive::owner::Owner::new().with(|| {
::leptos::reactive::owner::Owner::with_hydration(move || {
::leptos::tachys::reactive_graph::OwnedView::new({
#body_name(#prop_names)
})
})
::leptos::reactive::owner::Owner::with_hydration(move || {
#body_name(#prop_names)
})
}
} else {
@@ -327,8 +301,8 @@ impl ToTokens for Model {
let hydrate_fn_name = hydrate_fn_name.as_ref().unwrap();
quote! {
{
if ::leptos::context::use_context::<::leptos::reactive::owner::IsHydrating>()
.map(|h| h.0)
if ::leptos::reactive::owner::Owner::current_shared_context()
.map(|sc| sc.get_is_hydrating())
.unwrap_or(false) {
::leptos::either::Either::Left(
#component
@@ -365,23 +339,9 @@ impl ToTokens for Model {
let children = Box::new(|| {
let sc = ::leptos::reactive::owner::Owner::current_shared_context().unwrap();
let prev = sc.get_is_hydrating();
let owner = ::leptos::reactive::owner::Owner::new();
let value = owner.clone().with(|| {
::leptos::reactive::owner::Owner::with_no_hydration(move || {
::leptos::tachys::reactive_graph::OwnedView::new({
::leptos::tachys::html::islands::IslandChildren::new_with_on_hydrate(
children(),
{
let owner = owner.clone();
move || {
owner.set()
}
}
)
}).into_any()
})
});
let value = ::leptos::reactive::owner::Owner::with_no_hydration(||
::leptos::tachys::html::islands::IslandChildren::new(children()).into_any()
);
sc.set_is_hydrating(prev);
value
});
@@ -464,21 +424,20 @@ impl ToTokens for Model {
};
let children = if is_island_with_children {
quote! {
.children({
let owner = leptos::reactive::owner::Owner::current();
Box::new(move || {
.children({Box::new(|| {
use leptos::tachys::view::any_view::IntoAny;
::leptos::tachys::html::islands::IslandChildren::new_with_on_hydrate(
(),
{
let owner = owner.clone();
move || {
if let Some(owner) = &owner {
owner.set()
}
}
}
::leptos::tachys::html::islands::IslandChildren::new(
// TODO owner restoration for context
()
).into_any()})})
//.children(children)
/*.children(Box::new(|| {
use leptos::tachys::view::any_view::IntoAny;
::leptos::tachys::html::islands::IslandChildren::new(
// TODO owner restoration for context
()
).into_any()
}))*/
}
} else {
quote! {}
@@ -696,44 +655,14 @@ impl Prop {
abort!(e.span(), e.to_string());
});
let name = match *typed.pat {
Pat::Ident(i) => {
if let Some(name) = &prop_opts.name {
PatIdent {
attrs: vec![],
by_ref: None,
mutability: None,
ident: Ident::new(name, i.span()),
subpat: None,
}
} else {
i
}
}
Pat::Struct(_) | Pat::Tuple(_) | Pat::TupleStruct(_) => {
if let Some(name) = &prop_opts.name {
PatIdent {
attrs: vec![],
by_ref: None,
mutability: None,
ident: Ident::new(name, typed.pat.span()),
subpat: None,
}
} else {
abort!(
typed.pat,
"destructured props must be given a name e.g. \
#[prop(name = \"data\")]"
);
}
}
_ => {
abort!(
typed.pat,
"only `prop: bool` style types are allowed within the \
`#[component]` macro"
);
}
let name = if let Pat::Ident(i) = *typed.pat {
i
} else {
abort!(
typed.pat,
"only `prop: bool` style types are allowed within the \
`#[component]` macro"
);
};
Self {
@@ -936,7 +865,6 @@ struct PropOpt {
default: Option<syn::Expr>,
into: bool,
attrs: bool,
name: Option<String>,
}
struct TypedBuilderOpts {

View File

@@ -639,7 +639,7 @@ pub fn island(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
abort!(
transparent,
"only `transparent` is supported";
help = "try `#[island(transparent)]` or `#[island]`"
help = "try `#[component(transparent)]` or `#[component]`"
);
}

View File

@@ -108,12 +108,9 @@ pub(crate) fn component_to_tokens(
let KeyedAttributeValue::Binding(binding) = &attr.possible_value
else {
if let Some(ident) = attr.key.to_string().strip_prefix("let:") {
let span = match &attr.key {
NodeName::Punctuated(path) => path[1].span(),
_ => unreachable!(),
};
let ident1 = format_ident!("{ident}", span = span);
return Some(quote_spanned! { span => #ident1 });
let ident1 =
format_ident!("{ident}", span = attr.key.span());
return Some(quote! { #ident1 });
} else {
return None;
}

View File

@@ -652,18 +652,6 @@ pub(crate) fn element_to_tokens(
},
_ => None,
};
if let NodeAttribute::Attribute(a) = a {
if let Some(Tuple(_)) = a.value() {
return Ordering::Greater;
}
}
if let NodeAttribute::Attribute(b) = b {
if let Some(Tuple(_)) = b.value() {
return Ordering::Less;
}
}
match (key_a.as_deref(), key_b.as_deref()) {
(Some("class"), Some("class")) | (Some("style"), Some("style")) => {
Ordering::Equal
@@ -767,7 +755,7 @@ pub(crate) fn element_to_tokens(
let name = node.name().to_string();
// link custom ident to name span for IDE docs
let custom = Ident::new("custom", name.span());
quote_spanned! { node.name().span() => ::leptos::tachys::html::element::#custom(#name) }
quote! { ::leptos::tachys::html::element::#custom(#name) }
} else if is_svg_element(&tag) {
parent_type = TagType::Svg;
let name = if tag == "use" || tag == "use_" {
@@ -775,33 +763,33 @@ pub(crate) fn element_to_tokens(
} else {
name.to_token_stream()
};
quote_spanned! { node.name().span() => ::leptos::tachys::svg::#name() }
quote! { ::leptos::tachys::svg::#name() }
} else if is_math_ml_element(&tag) {
parent_type = TagType::Math;
quote_spanned! { node.name().span() => ::leptos::tachys::mathml::#name() }
quote! { ::leptos::tachys::mathml::#name() }
} else if is_ambiguous_element(&tag) {
match parent_type {
TagType::Unknown => {
// We decided this warning was too aggressive, but I'll leave it here in case we want it later
/* proc_macro_error2::emit_warning!(name.span(), "The view macro is assuming this is an HTML element, \
but it is ambiguous; if it is an SVG or MathML element, prefix with svg:: or math::"); */
quote_spanned! { node.name().span() =>
quote! {
::leptos::tachys::html::element::#name()
}
}
TagType::Html => {
quote_spanned! { node.name().span() => ::leptos::tachys::html::element::#name() }
quote! { ::leptos::tachys::html::element::#name() }
}
TagType::Svg => {
quote_spanned! { node.name().span() => ::leptos::tachys::svg::#name() }
quote! { ::leptos::tachys::svg::#name() }
}
TagType::Math => {
quote_spanned! { node.name().span() => ::leptos::tachys::math::#name() }
quote! { ::leptos::tachys::math::#name() }
}
}
} else {
parent_type = TagType::Html;
quote_spanned! { name.span() => ::leptos::tachys::html::element::#name() }
quote! { ::leptos::tachys::html::element::#name() }
};
/* TODO restore this
@@ -1651,7 +1639,7 @@ pub(crate) fn ident_from_tag_name(tag_name: &NodeName) -> Ident {
.path
.segments
.iter()
.next_back()
.last()
.map(|segment| segment.ident.clone())
.expect("element needs to have a name"),
NodeName::Block(_) => {
@@ -1722,7 +1710,7 @@ fn tuple_name(name: &str, node: &KeyedAttribute) -> TupleName {
TupleName::None
}
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug)]
enum TupleName {
None,
Str(String),

View File

@@ -1,15 +1,6 @@
use core::num::NonZeroUsize;
use leptos::prelude::*;
#[derive(PartialEq, Debug)]
struct UserInfo {
user_id: String,
email: String,
}
#[derive(PartialEq, Debug)]
struct Admin(bool);
#[component]
fn Component(
#[prop(optional)] optional: bool,
@@ -19,10 +10,6 @@ fn Component(
#[prop(default = NonZeroUsize::new(10).unwrap())] default: NonZeroUsize,
#[prop(into)] into: String,
impl_trait: impl Fn() -> i32 + 'static,
#[prop(name = "data")] UserInfo { email, user_id }: UserInfo,
#[prop(name = "tuple")] (name, id): (String, i32),
#[prop(name = "tuple_struct")] Admin(is_admin): Admin,
#[prop(name = "outside_name")] inside_name: i32,
) -> impl IntoView {
_ = optional;
_ = optional_into;
@@ -31,12 +18,6 @@ fn Component(
_ = default;
_ = into;
_ = impl_trait;
_ = email;
_ = user_id;
_ = id;
_ = name;
_ = is_admin;
_ = inside_name;
}
#[test]
@@ -45,13 +26,6 @@ fn component() {
.into("")
.strip_option(9)
.impl_trait(|| 42)
.data(UserInfo {
email: "em@il".into(),
user_id: "1".into(),
})
.tuple(("Joe".into(), 12))
.tuple_struct(Admin(true))
.outside_name(1)
.build();
assert!(!cp.optional);
assert_eq!(cp.optional_into, None);
@@ -60,16 +34,6 @@ fn component() {
assert_eq!(cp.default, NonZeroUsize::new(10).unwrap());
assert_eq!(cp.into, "");
assert_eq!((cp.impl_trait)(), 42);
assert_eq!(
cp.data,
UserInfo {
email: "em@il".into(),
user_id: "1".into(),
}
);
assert_eq!(cp.tuple, ("Joe".into(), 12));
assert_eq!(cp.tuple_struct, Admin(true));
assert_eq!(cp.outside_name, 1);
}
#[test]
@@ -81,26 +45,12 @@ fn component_nostrip() {
strip_option=9
into=""
impl_trait=|| 42
data=UserInfo {
email: "em@il".into(),
user_id: "1".into(),
}
tuple=("Joe".into(), 12)
tuple_struct=Admin(true)
outside_name=1
/>
<Component
nostrip:optional_into=Some("foo")
strip_option=9
into=""
impl_trait=|| 42
data=UserInfo {
email: "em@il".into(),
user_id: "1".into(),
}
tuple=("Joe".into(), 12)
tuple_struct=Admin(true)
outside_name=1
/>
};
}

View File

@@ -44,10 +44,4 @@ fn default_with_invalid_value(
_ = default;
}
#[component]
fn destructure_without_name((default, value): (bool, i32)) -> impl IntoView {
_ = default;
_ = value;
}
fn main() {}

View File

@@ -1,4 +1,4 @@
error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default`, `into`, `attrs` and `name`
error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default`, `into` and `attrs`
--> tests/ui/component.rs:10:31
|
10 | fn unknown_prop_option(#[prop(hello)] test: bool) -> impl IntoView {
@@ -41,9 +41,3 @@ error: unexpected end of input, expected one of: identifier, `::`, `<`, `_`, lit
| ^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `component` (in Nightly builds, run with -Z macro-backtrace for more info)
error: destructured props must be given a name e.g. #[prop(name = "data")]
--> tests/ui/component.rs:48:29
|
48 | fn destructure_without_name((default, value): (bool, i32)) -> impl IntoView {
| ^^^^^^^^^^^^^^^^

View File

@@ -1,4 +1,4 @@
error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default`, `into`, `attrs` and `name`
error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default`, `into` and `attrs`
--> tests/ui/component_absolute.rs:5:31
|
5 | fn unknown_prop_option(#[prop(hello)] test: bool) -> impl ::leptos::IntoView {

View File

@@ -15,7 +15,7 @@ codee = { version = "0.2.0", features = ["json_serde"] }
hydration_context = { workspace = true }
reactive_graph = { workspace = true, features = ["hydration"] }
server_fn = { workspace = true }
tracing = { version = "0.1.41", optional = true }
tracing = { version = "0.1.40", optional = true }
futures = "0.3.31"
any_spawner = { workspace = true }
@@ -25,8 +25,8 @@ send_wrapper = "0.6"
# serialization formats
serde = { version = "1.0" }
js-sys = { version = "0.3.74", optional = true }
wasm-bindgen = { version = "0.2.97", optional = true }
js-sys = { version = "0.3.72", optional = true }
wasm-bindgen = { version = "0.2.95", optional = true }
serde_json = { version = "1.0" }
[features]
@@ -44,6 +44,3 @@ denylist = ["tracing"]
[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(leptos_debuginfo)'] }

View File

@@ -43,7 +43,7 @@ where
S::Output: 'static,
{
inner: ArcAction<S, Result<S::Output, ServerFnError<S::Error>>>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -65,7 +65,7 @@ where
inner: ArcAction::new_with_value(err, |input: &S| {
S::run_on_client(input.clone())
}),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -91,7 +91,7 @@ where
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
}
}
@@ -114,11 +114,11 @@ where
S::Output: 'static,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -132,7 +132,7 @@ where
S::Output: 'static,
{
inner: Action<S, Result<S::Output, ServerFnError<S::Error>>>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -153,7 +153,7 @@ where
inner: Action::new_with_value(err, |input: &S| {
S::run_on_client(input.clone())
}),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -217,11 +217,11 @@ where
S::Output: 'static,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}

View File

@@ -8,11 +8,8 @@ use reactive_graph::{
ToAnySource, ToAnySubscriber,
},
owner::use_context,
signal::{
guards::{AsyncPlain, ReadGuard},
ArcRwSignal, RwSignal,
},
traits::{DefinedAt, IsDisposed, ReadUntracked, Track, Update, Write},
signal::guards::{AsyncPlain, ReadGuard},
traits::{DefinedAt, IsDisposed, ReadUntracked},
};
use send_wrapper::SendWrapper;
use std::{
@@ -23,8 +20,7 @@ use std::{
/// A reference-counted resource that only loads its data locally on the client.
pub struct ArcLocalResource<T> {
data: ArcAsyncDerived<SendWrapper<T>>,
refetch: ArcRwSignal<usize>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -32,8 +28,7 @@ impl<T> Clone for ArcLocalResource<T> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
refetch: self.refetch.clone(),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
}
}
@@ -70,27 +65,15 @@ impl<T> ArcLocalResource<T> {
}
};
let fetcher = SendWrapper::new(fetcher);
let refetch = ArcRwSignal::new(0);
let data = {
let refetch = refetch.clone();
ArcAsyncDerived::new(move || {
refetch.track();
Self {
data: ArcAsyncDerived::new(move || {
let fut = fetcher();
SendWrapper::new(async move { SendWrapper::new(fut.await) })
})
};
Self {
data,
refetch,
#[cfg(any(debug_assertions, leptos_debuginfo))]
}),
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
/// Re-runs the async function.
pub fn refetch(&self) {
*self.refetch.write() += 1;
}
}
impl<T> IntoFuture for ArcLocalResource<T>
@@ -121,11 +104,11 @@ where
impl<T> DefinedAt for ArcLocalResource<T> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -217,8 +200,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>>,
refetch: RwSignal<usize>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -260,7 +242,6 @@ impl<T> LocalResource<T> {
}
}
};
let refetch = RwSignal::new(0);
Self {
data: if cfg!(feature = "ssr") {
@@ -268,21 +249,14 @@ impl<T> LocalResource<T> {
} else {
let fetcher = SendWrapper::new(fetcher);
AsyncDerived::new(move || {
refetch.track();
let fut = fetcher();
SendWrapper::new(async move { SendWrapper::new(fut.await) })
})
},
refetch,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
/// Re-runs the async function.
pub fn refetch(&self) {
self.refetch.try_update(|n| *n += 1);
}
}
impl<T> IntoFuture for LocalResource<T>
@@ -313,11 +287,11 @@ where
impl<T> DefinedAt for LocalResource<T> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -424,8 +398,7 @@ impl<T: 'static> From<ArcLocalResource<T>> for LocalResource<T> {
fn from(arc: ArcLocalResource<T>) -> Self {
Self {
data: arc.data.into(),
refetch: arc.refetch.into(),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: arc.defined_at,
}
}
@@ -435,8 +408,7 @@ impl<T: 'static> From<LocalResource<T>> for ArcLocalResource<T> {
fn from(local: LocalResource<T>) -> Self {
Self {
data: local.data.into(),
refetch: local.refetch.into(),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: local.defined_at,
}
}

View File

@@ -12,7 +12,7 @@ where
S::Output: 'static,
{
inner: ArcMultiAction<S, Result<S::Output, ServerFnError<S::Error>>>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -29,7 +29,7 @@ where
inner: ArcMultiAction::new(|input: &S| {
S::run_on_client(input.clone())
}),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -55,7 +55,7 @@ where
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
}
}
@@ -78,11 +78,11 @@ where
S::Output: 'static,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -96,7 +96,7 @@ where
S::Output: 'static,
{
inner: MultiAction<S, Result<S::Output, ServerFnError<S::Error>>>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -123,7 +123,7 @@ where
inner: MultiAction::new(|input: &S| {
S::run_on_client(input.clone())
}),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -176,11 +176,11 @@ where
S::Output: 'static,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}

View File

@@ -60,7 +60,7 @@ pub struct ArcOnceResource<T, Ser = JsonSerdeCodec> {
suspenses: Arc<RwLock<Vec<SuspenseContext>>>,
loading: Arc<AtomicBool>,
ser: PhantomData<fn() -> Ser>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -73,7 +73,7 @@ impl<T, Ser> Clone for ArcOnceResource<T, Ser> {
suspenses: self.suspenses.clone(),
loading: self.loading.clone(),
ser: self.ser,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
}
}
@@ -140,7 +140,7 @@ where
wakers,
suspenses,
ser: PhantomData,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
};
@@ -183,11 +183,11 @@ impl<T, Ser> ArcOnceResource<T, Ser> {
impl<T, Ser> DefinedAt for ArcOnceResource<T, Ser> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
@@ -272,7 +272,7 @@ where
#[track_caller]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
let _guard = SpecialNonReactiveZone::enter();
let waker = cx.waker();
self.source.track();
@@ -491,7 +491,7 @@ where
#[derive(Debug)]
pub struct OnceResource<T, Ser = JsonSerdeCodec> {
inner: ArenaItem<ArcOnceResource<T, Ser>>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -524,13 +524,13 @@ where
fut: impl Future<Output = T> + Send + 'static,
blocking: bool,
) -> Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
let defined_at = Location::caller();
Self {
inner: ArenaItem::new(ArcOnceResource::new_with_options(
fut, blocking,
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at,
}
}
@@ -551,11 +551,11 @@ where
impl<T, Ser> DefinedAt for OnceResource<T, Ser> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}

View File

@@ -74,7 +74,7 @@ pub struct ArcResource<T, Ser = JsonSerdeCodec> {
ser: PhantomData<Ser>,
refetch: ArcRwSignal<usize>,
data: ArcAsyncDerived<T>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -82,7 +82,7 @@ impl<T, Ser> Debug for ArcResource<T, Ser> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut d = f.debug_struct("ArcResource");
d.field("ser", &self.ser).field("data", &self.data);
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
d.field("defined_at", self.defined_at);
d.finish_non_exhaustive()
}
@@ -98,7 +98,7 @@ where
ser: PhantomData,
data: arc_resource.data.into(),
refetch: arc_resource.refetch.into(),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -114,7 +114,7 @@ where
ser: PhantomData,
data: resource.data.into(),
refetch: resource.refetch.into(),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -122,11 +122,11 @@ where
impl<T, Ser> DefinedAt for ArcResource<T, Ser> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -139,7 +139,7 @@ impl<T, Ser> Clone for ArcResource<T, Ser> {
ser: self.ser,
refetch: self.refetch.clone(),
data: self.data.clone(),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
}
}
@@ -300,7 +300,7 @@ where
ser: PhantomData,
data,
refetch,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -781,7 +781,7 @@ where
ser: PhantomData<Ser>,
data: AsyncDerived<T>,
refetch: RwSignal<usize>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -792,7 +792,7 @@ where
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut d = f.debug_struct("ArcResource");
d.field("ser", &self.ser).field("data", &self.data);
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
d.field("defined_at", self.defined_at);
d.finish_non_exhaustive()
}
@@ -803,11 +803,11 @@ where
T: Send + Sync + 'static,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -1270,7 +1270,7 @@ where
ser: PhantomData,
data: data.into(),
refetch: refetch.into(),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos_meta"
version = "0.7.4"
version = "0.7.0-rc2"
authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
@@ -14,8 +14,8 @@ once_cell = "1.20"
or_poisoned = { workspace = true }
indexmap = "2.6"
send_wrapper = "0.6.0"
tracing = { version = "0.1.41", optional = true }
wasm-bindgen = "0.2.97"
tracing = { version = "0.1.40", optional = true }
wasm-bindgen = "0.2.95"
futures = "0.3.31"
[dependencies.web-sys]
@@ -26,13 +26,9 @@ features = ["HtmlLinkElement", "HtmlMetaElement", "HtmlTitleElement"]
default = []
ssr = []
tracing = ["dep:tracing"]
nonce = ["leptos/nonce"]
[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]
[package.metadata.cargo-all-features]
denylist = ["tracing"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(leptos_debuginfo)'] }

View File

@@ -37,17 +37,20 @@
//! }
//! ```
//! # Feature Flags
//! - `csr` Client-side rendering: Generate DOM nodes in the browser
//! - `ssr` Server-side rendering: Generate an HTML string (typically on the server)
//! - `tracing` Adds integration with the `tracing` crate.
//! - `hydrate` Hydration: use this to add interactivity to an SSRed Leptos app
//! - `stable` By default, Leptos requires `nightly` Rust, which is what allows the ergonomics
//! of calling signals as functions. Enable this feature to support `stable` Rust.
//!
//! **Important Note:** If youre using server-side rendering, you should enable `ssr`.
//! **Important Note:** You must enable one of `csr`, `hydrate`, or `ssr` to tell Leptos
//! which mode your app is operating in.
use futures::{Stream, StreamExt};
use leptos::{
attr::NextAttribute,
component,
logging::debug_warn,
oco::Oco,
reactive::owner::{provide_context, use_context},
tachys::{
dom::document,
@@ -567,25 +570,3 @@ impl RenderHtml for MetaTagsView {
) -> Self::State {
}
}
pub(crate) trait OrDefaultNonce {
fn or_default_nonce(self) -> Option<Oco<'static, str>>;
}
impl OrDefaultNonce for Option<Oco<'static, str>> {
fn or_default_nonce(self) -> Option<Oco<'static, str>> {
#[cfg(feature = "nonce")]
{
use leptos::nonce::use_nonce;
match self {
Some(nonce) => Some(nonce),
None => use_nonce().map(|n| Arc::clone(n.as_inner()).into()),
}
}
#[cfg(not(feature = "nonce"))]
{
self
}
}
}

View File

@@ -1,4 +1,4 @@
use crate::{register, OrDefaultNonce};
use crate::register;
use leptos::{
component, oco::Oco, prelude::*, tachys::html::element::script, IntoView,
};
@@ -74,7 +74,7 @@ pub fn Script(
.fetchpriority(fetchpriority)
.integrity(integrity)
.nomodule(nomodule)
.nonce(nonce.or_default_nonce())
.nonce(nonce)
.referrerpolicy(referrerpolicy)
.src(src)
.r#type(type_)

View File

@@ -1,4 +1,4 @@
use crate::{register, OrDefaultNonce};
use crate::register;
use leptos::{
component, oco::Oco, prelude::*, tachys::html::element::style, IntoView,
};
@@ -48,7 +48,7 @@ pub fn Style(
style()
.id(id)
.media(media)
.nonce(nonce.or_default_nonce())
.nonce(nonce)
.title(title)
.blocking(blocking)
.child(children.map(|c| c())),

View File

@@ -1,6 +1,6 @@
[package]
name = "next_tuple"
version = "0.1.0"
version = "0.1.0-rc2"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -1,6 +1,6 @@
[package]
name = "reactive_graph"
version = "0.1.4"
version = "0.1.0-rc2"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"
@@ -19,13 +19,13 @@ rustc-hash = "2.0"
serde = { version = "1.0", features = ["derive"], optional = true }
slotmap = "1.0"
thiserror = "2.0"
tracing = { version = "0.1.41", optional = true }
tracing = { version = "0.1.40", optional = true }
guardian = "1.2"
async-lock = "3.4.0"
send_wrapper = { version = "0.6.0", features = ["futures"] }
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
web-sys = { version = "0.3.72", features = ["console"] }
web-sys = "0.3.72"
[dev-dependencies]
tokio = { version = "1.41", features = ["rt-multi-thread", "macros"] }
@@ -47,6 +47,3 @@ rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.cargo-all-features]
denylist = ["tracing"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(leptos_debuginfo)'] }

View File

@@ -101,7 +101,7 @@ pub struct ArcAction<I, O> {
action_fn: Arc<
dyn Fn(&I) -> Pin<Box<dyn Future<Output = O> + Send>> + Send + Sync,
>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -114,7 +114,7 @@ impl<I, O> Clone for ArcAction<I, O> {
version: self.version.clone(),
dispatched: self.dispatched.clone(),
action_fn: self.action_fn.clone(),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
}
}
@@ -198,7 +198,7 @@ where
version: Default::default(),
dispatched: Default::default(),
action_fn: Arc::new(move |input| Box::pin(action_fn(input))),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -370,7 +370,7 @@ where
action_fn: Arc::new(move |input| {
Box::pin(SendWrapper::new(action_fn(input)))
}),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -502,11 +502,11 @@ where
O: 'static,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -592,7 +592,7 @@ where
/// ```
pub struct Action<I, O, S = SyncStorage> {
inner: ArenaItem<ArcAction<I, O>, S>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -656,7 +656,7 @@ where
{
Self {
inner: ArenaItem::new(ArcAction::new(action_fn)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -681,7 +681,7 @@ where
{
Self {
inner: ArenaItem::new(ArcAction::new_with_value(value, action_fn)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -715,11 +715,11 @@ where
pub fn new_local<F, Fu>(action_fn: F) -> Self
where
F: Fn(&I) -> Fu + 'static,
Fu: Future<Output = O> + 'static,
Fu: Future<Output = O> + Send + 'static,
{
Self {
inner: ArenaItem::new_local(ArcAction::new_unsync(action_fn)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -737,7 +737,7 @@ where
inner: ArenaItem::new_local(ArcAction::new_unsync_with_value(
value, action_fn,
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -979,7 +979,7 @@ where
inner: ArenaItem::new_with_storage(ArcAction::new_unsync(
action_fn,
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -998,7 +998,7 @@ where
inner: ArenaItem::new_with_storage(
ArcAction::new_unsync_with_value(value, action_fn),
),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -1006,11 +1006,11 @@ where
impl<I, O, S> DefinedAt for Action<I, O, S> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}

View File

@@ -46,7 +46,7 @@ use std::{fmt::Debug, future::Future, panic::Location, pin::Pin, sync::Arc};
/// ```
pub struct MultiAction<I, O, S = SyncStorage> {
inner: ArenaItem<ArcMultiAction<I, O>, S>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -62,11 +62,11 @@ where
O: 'static,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -130,7 +130,7 @@ where
{
Self {
inner: ArenaItem::new_with_storage(ArcMultiAction::new(action_fn)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}

View File

@@ -93,7 +93,7 @@ pub struct ArcMemo<T, S = SyncStorage>
where
S: Storage<T>,
{
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
inner: Arc<RwLock<MemoInner<T, S>>>,
}
@@ -164,7 +164,7 @@ where
RwLock::new(MemoInner::new(Arc::new(fun), subscriber))
});
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner,
}
@@ -177,7 +177,7 @@ where
{
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
inner: Arc::clone(&self.inner),
}
@@ -190,11 +190,11 @@ where
{
#[inline(always)]
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -272,7 +272,7 @@ where
AnySource(
Arc::as_ptr(&self.inner) as usize,
Arc::downgrade(&self.inner) as Weak<dyn Source + Send + Sync>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
self.defined_at,
)
}

View File

@@ -107,7 +107,7 @@ use std::{
/// - [`IntoFuture`](std::future::Future) allows you to create a [`Future`] that resolves
/// when this resource is done loading.
pub struct ArcAsyncDerived<T> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static Location<'static>,
// the current state of this signal
pub(crate) value: Arc<AsyncRwLock<Option<T>>>,
@@ -184,7 +184,7 @@ impl<T> BlockingLock<T> for AsyncRwLock<T> {
impl<T> Clone for ArcAsyncDerived<T> {
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
value: Arc::clone(&self.value),
wakers: Arc::clone(&self.wakers),
@@ -197,7 +197,7 @@ impl<T> Clone for ArcAsyncDerived<T> {
impl<T> Debug for ArcAsyncDerived<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut f = f.debug_struct("ArcAsyncDerived");
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
f.field("defined_at", &self.defined_at);
f.finish_non_exhaustive()
}
@@ -206,11 +206,11 @@ impl<T> Debug for ArcAsyncDerived<T> {
impl<T> DefinedAt for ArcAsyncDerived<T> {
#[inline(always)]
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -241,7 +241,7 @@ macro_rules! spawn_derived {
let wakers = Arc::new(RwLock::new(Vec::new()));
let this = ArcAsyncDerived {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
value: Arc::clone(&value),
wakers,
@@ -272,10 +272,7 @@ macro_rules! spawn_derived {
// so that the correct value is set synchronously
let initial = initial_fut.as_mut().now_or_never();
match initial {
None => {
inner.write().or_poisoned().notifier.notify();
(false, Some(initial_fut))
}
None => (false, Some(initial_fut)),
Some(orig_value) => {
let mut guard = this.inner.write().or_poisoned();
@@ -299,6 +296,10 @@ macro_rules! spawn_derived {
if was_ready {
first_run.take();
}
// begin loading eagerly but asynchronously, if not already loaded
if !was_ready {
any_subscriber.mark_dirty();
}
if let Some(source) = $source {
any_subscriber.with_observer(|| source.track());
@@ -311,18 +312,6 @@ macro_rules! spawn_derived {
let wakers = Arc::downgrade(&this.wakers);
let loading = Arc::downgrade(&this.loading);
let fut = async move {
// if the AsyncDerived has *already* been marked dirty (i.e., one of its
// sources has changed after creation), we should throw out the Future
// we already created, because its values might be stale
let already_dirty = inner.upgrade()
.as_ref()
.and_then(|inner| inner.read().ok())
.map(|inner| inner.state == AsyncDerivedState::Dirty)
.unwrap_or(false);
if already_dirty {
initial_fut.take();
}
while rx.next().await.is_some() {
let update_if_necessary = if $should_track {
any_subscriber
@@ -424,10 +413,7 @@ impl<T: 'static> ArcAsyncDerived<T> {
) {
loading.store(false, Ordering::Relaxed);
let prev_state = mem::replace(
&mut inner.write().or_poisoned().state,
AsyncDerivedState::Notifying,
);
inner.write().or_poisoned().state = AsyncDerivedState::Notifying;
if let Some(ready_tx) = ready_tx {
// if it's an Err, that just means the Receiver was dropped
@@ -447,10 +433,7 @@ impl<T: 'static> ArcAsyncDerived<T> {
waker.wake();
}
// if this was marked dirty before notifications began, this means it
// had been notified while loading; marking it clean will cause it not to
// run on the next tick of the async loop, so here it should be left dirty
inner.write().or_poisoned().state = prev_state;
inner.write().or_poisoned().state = AsyncDerivedState::Clean;
}
}
@@ -649,7 +632,7 @@ impl<T: 'static> ToAnySource for ArcAsyncDerived<T> {
AnySource(
Arc::as_ptr(&self.inner) as usize,
Arc::downgrade(&self.inner) as Weak<dyn Source + Send + Sync>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
self.defined_at,
)
}

View File

@@ -83,7 +83,7 @@ use std::{future::Future, ops::DerefMut, panic::Location};
/// - [`IntoFuture`](std::future::Future) allows you to create a [`Future`] that resolves
/// when this resource is done loading.
pub struct AsyncDerived<T, S = SyncStorage> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
pub(crate) inner: ArenaItem<ArcAsyncDerived<T>, S>,
}
@@ -99,10 +99,10 @@ where
T: Send + Sync + 'static,
{
fn from(value: ArcAsyncDerived<T>) -> Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
let defined_at = value.defined_at;
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at,
inner: ArenaItem::new_with_storage(value),
}
@@ -127,10 +127,10 @@ where
T: 'static,
{
fn from_local(value: ArcAsyncDerived<T>) -> Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
let defined_at = value.defined_at;
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at,
inner: ArenaItem::new_with_storage(value),
}
@@ -152,7 +152,7 @@ where
Fut: Future<Output = T> + Send + 'static,
{
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcAsyncDerived::new(fun)),
}
@@ -170,7 +170,7 @@ where
Fut: Future<Output = T> + Send + 'static,
{
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(
ArcAsyncDerived::new_with_initial(initial_value, fun),
@@ -187,7 +187,7 @@ impl<T> AsyncDerived<SendWrapper<T>> {
Fut: Future<Output = T> + 'static,
{
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcAsyncDerived::new_mock(fun)),
}
@@ -209,7 +209,7 @@ where
Fut: Future<Output = T> + 'static,
{
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcAsyncDerived::new_unsync(
fun,
@@ -230,7 +230,7 @@ where
Fut: Future<Output = T> + 'static,
{
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(
ArcAsyncDerived::new_unsync_with_initial(initial_value, fun),
@@ -278,11 +278,11 @@ where
impl<T, S> DefinedAt for AsyncDerived<T, S> {
#[inline(always)]
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}

View File

@@ -67,9 +67,7 @@ where
fn mark_check(&self) {
{
let mut lock = self.write().or_poisoned();
if lock.state != ReactiveNodeState::Dirty {
lock.state = ReactiveNodeState::Check;
}
lock.state = ReactiveNodeState::Check;
}
for sub in (&self.read().or_poisoned().subscribers).into_iter() {
sub.mark_check();

View File

@@ -100,7 +100,7 @@ pub struct Memo<T, S = SyncStorage>
where
S: Storage<T>,
{
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
inner: ArenaItem<ArcMemo<T, S>, S>,
}
@@ -121,7 +121,7 @@ where
#[track_caller]
fn from(value: ArcMemo<T, SyncStorage>) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
@@ -135,7 +135,7 @@ where
#[track_caller]
fn from_local(value: ArcMemo<T, LocalStorage>) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
@@ -175,7 +175,7 @@ where
T: PartialEq,
{
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcMemo::new(fun)),
}
@@ -197,7 +197,7 @@ where
changed: fn(Option<&T>, Option<&T>) -> bool,
) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcMemo::new_with_compare(
fun, changed,
@@ -207,7 +207,7 @@ where
/// Creates a new memo by passing a function that computes the value.
///
/// Unlike [`Memo::new`](), this receives ownership of the previous value. As a result, it
/// Unlike [`ArcMemo::new`](), this receives ownership of the previous value. As a result, it
/// must return both the new value and a `bool` that is `true` if the value has changed.
///
/// This is lazy: the function will not be called until the memo's value is read for the first
@@ -221,7 +221,7 @@ where
fun: impl Fn(Option<T>) -> (T, bool) + Send + Sync + 'static,
) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcMemo::new_owning(fun)),
}
@@ -276,11 +276,11 @@ where
S: Storage<T>,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}

View File

@@ -59,7 +59,7 @@ impl<'a> IntoIterator for &'a SourceSet {
self.0.iter()
}
}
#[derive(Debug, Default, Clone)]
#[derive(Default, Clone)]
pub struct SubscriberSet(Vec<AnySubscriber>);
impl SubscriberSet {

View File

@@ -26,17 +26,16 @@ pub trait Source: ReactiveNode {
pub struct AnySource(
pub(crate) usize,
pub(crate) Weak<dyn Source + Send + Sync>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
pub(crate) &'static Location<'static>,
#[cfg(debug_assertions)] pub(crate) &'static Location<'static>,
);
impl DefinedAt for AnySource {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.2)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}

View File

@@ -292,8 +292,6 @@ impl Owner {
#[cfg(feature = "hydration")]
pub fn with_hydration<T>(fun: impl FnOnce() -> T + 'static) -> T {
fn inner<T>(fun: Box<dyn FnOnce() -> T>) -> T {
provide_context(IsHydrating(true));
let sc = OWNER.with_borrow(|o| {
o.as_ref()
.and_then(|current| current.shared_context.clone())
@@ -317,8 +315,6 @@ impl Owner {
#[cfg(feature = "hydration")]
pub fn with_no_hydration<T>(fun: impl FnOnce() -> T + 'static) -> T {
fn inner<T>(fun: Box<dyn FnOnce() -> T>) -> T {
provide_context(IsHydrating(false));
let sc = OWNER.with_borrow(|o| {
o.as_ref()
.and_then(|current| current.shared_context.clone())
@@ -339,10 +335,6 @@ impl Owner {
}
}
#[doc(hidden)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct IsHydrating(pub bool);
/// Registers a function to be run the next time the current owner is cleaned up.
///
/// Because the ownership model is associated with reactive nodes, each "decision point" in an

View File

@@ -1,6 +1,6 @@
use crate::{
signal::guards::{Plain, ReadGuard, UntrackedWriteGuard},
traits::{DefinedAt, IntoInner, IsDisposed, ReadValue, WriteValue},
traits::{DefinedAt, IsDisposed, ReadValue, WriteValue},
};
use std::{
fmt::{Debug, Formatter},
@@ -19,7 +19,7 @@ use std::{
/// accessing it does not cause effects to subscribe, and
/// updating it does not notify anything else.
pub struct ArcStoredValue<T> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
value: Arc<RwLock<T>>,
}
@@ -27,7 +27,7 @@ pub struct ArcStoredValue<T> {
impl<T> Clone for ArcStoredValue<T> {
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
value: Arc::clone(&self.value),
}
@@ -47,7 +47,7 @@ impl<T: Default> Default for ArcStoredValue<T> {
#[track_caller]
fn default() -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
value: Arc::new(RwLock::new(T::default())),
}
@@ -70,11 +70,11 @@ impl<T> Hash for ArcStoredValue<T> {
impl<T> DefinedAt for ArcStoredValue<T> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -90,7 +90,7 @@ impl<T> ArcStoredValue<T> {
#[track_caller]
pub fn new(value: T) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
value: Arc::new(RwLock::new(value)),
}
@@ -124,12 +124,3 @@ impl<T> IsDisposed for ArcStoredValue<T> {
false
}
}
impl<T> IntoInner for ArcStoredValue<T> {
type Value = T;
#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
Some(Arc::into_inner(self.value)?.into_inner().unwrap())
}
}

View File

@@ -121,11 +121,8 @@ pub mod sandboxed {
}
impl<T> Sandboxed<T> {
/// Wraps the given [`Future`], ensuring that any [`ArenaItem`][item] created while it is
/// being polled will be associated with the same arena that was active when this was
/// called.
///
/// [item]:[crate::owner::ArenaItem]
/// Wraps the given [`Future`], ensuring that any [`ArenaItem`] created while it is being
/// polled will be associated with the same arena that was active when this was called.
pub fn new(inner: T) -> Self {
let arena = MAP.with_borrow(|n| n.as_ref().and_then(Weak::upgrade));
Self { arena, inner }

View File

@@ -2,7 +2,7 @@ use super::{
arena::{Arena, NodeId},
LocalStorage, Storage, SyncStorage, OWNER,
};
use crate::traits::{Dispose, IntoInner, IsDisposed};
use crate::traits::{Dispose, IsDisposed};
use send_wrapper::SendWrapper;
use std::{any::Any, hash::Hash, marker::PhantomData};
@@ -134,12 +134,3 @@ impl<T, S> Dispose for ArenaItem<T, S> {
Arena::with_mut(|arena| arena.remove(self.node));
}
}
impl<T, S: Storage<T>> IntoInner for ArenaItem<T, S> {
type Value = T;
#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
S::take(self.node)
}
}

View File

@@ -15,14 +15,35 @@ impl Owner {
}
fn use_context<T: Clone + 'static>(&self) -> Option<T> {
self.with_context(Clone::clone)
let ty = TypeId::of::<T>();
let inner = self.inner.read().or_poisoned();
let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());
let contexts = &self.inner.read().or_poisoned().contexts;
if let Some(context) = contexts.get(&ty) {
context.downcast_ref::<T>().cloned()
} else {
while let Some(ref this_parent) = parent.clone() {
let this_parent = this_parent.read().or_poisoned();
let contexts = &this_parent.contexts;
let value = contexts.get(&ty);
let downcast = value
.and_then(|context| context.downcast_ref::<T>().cloned());
if let Some(value) = downcast {
return Some(value);
} else {
parent =
this_parent.parent.as_ref().and_then(|p| p.upgrade());
}
}
None
}
}
fn take_context<T: 'static>(&self) -> Option<T> {
let ty = TypeId::of::<T>();
let mut inner = self.inner.write().or_poisoned();
let inner = self.inner.read().or_poisoned();
let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());
let contexts = &mut inner.contexts;
let contexts = &mut self.inner.write().or_poisoned().contexts;
if let Some(context) = contexts.remove(&ty) {
context.downcast::<T>().ok().map(|n| *n)
} else {
@@ -43,64 +64,6 @@ impl Owner {
}
}
fn with_context<T: 'static, R>(
&self,
cb: impl FnOnce(&T) -> R,
) -> Option<R> {
let ty = TypeId::of::<T>();
let inner = self.inner.read().or_poisoned();
let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());
let contexts = &inner.contexts;
let reference = if let Some(context) = contexts.get(&ty) {
context.downcast_ref::<T>()
} else {
while let Some(ref this_parent) = parent.clone() {
let this_parent = this_parent.read().or_poisoned();
let contexts = &this_parent.contexts;
let value = contexts.get(&ty);
let downcast =
value.and_then(|context| context.downcast_ref::<T>());
if let Some(value) = downcast {
return Some(cb(value));
} else {
parent =
this_parent.parent.as_ref().and_then(|p| p.upgrade());
}
}
None
};
reference.map(cb)
}
fn update_context<T: 'static, R>(
&self,
cb: impl FnOnce(&mut T) -> R,
) -> Option<R> {
let ty = TypeId::of::<T>();
let mut inner = self.inner.write().or_poisoned();
let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());
let contexts = &mut inner.contexts;
let reference = if let Some(context) = contexts.get_mut(&ty) {
context.downcast_mut::<T>()
} else {
while let Some(ref this_parent) = parent.clone() {
let mut this_parent = this_parent.write().or_poisoned();
let contexts = &mut this_parent.contexts;
let value = contexts.get_mut(&ty);
let downcast =
value.and_then(|context| context.downcast_mut::<T>());
if let Some(value) = downcast {
return Some(cb(value));
} else {
parent =
this_parent.parent.as_ref().and_then(|p| p.upgrade());
}
}
None
};
reference.map(cb)
}
/// Searches for items stored in context in either direction, either among parents or among
/// descendants.
pub fn use_context_bidirectional<T: Clone + 'static>(&self) -> Option<T> {
@@ -356,86 +319,3 @@ pub fn expect_context<T: Clone + 'static>() -> T {
pub fn take_context<T: 'static>() -> Option<T> {
Owner::current().and_then(|owner| owner.take_context())
}
/// Access a reference to a context value of type `T` in the reactive system.
///
/// This traverses the reactive ownership graph, beginning from the current reactive
/// [`Owner`] and iterating through its parents, if any. When the value is found,
/// the function that you pass is applied to an immutable reference to it.
///
/// The context value should have been provided elsewhere using
/// [`provide_context`](provide_context).
///
/// This is useful for passing values down to components or functions lower in a
/// hierarchy without needs to “prop drill” by passing them through each layer as
/// arguments to a function or properties of a component.
///
/// Context works similarly to variable scope: a context that is provided higher in
/// the reactive graph can be used lower down, but a context that is provided lower
/// in the tree cannot be used higher up.
///
/// ```rust
/// # use reactive_graph::prelude::*;
/// # use reactive_graph::owner::*;
/// # let owner = Owner::new(); owner.set();
/// # use reactive_graph::effect::Effect;
/// # futures::executor::block_on(async move {
/// # any_spawner::Executor::init_futures_executor();
/// Effect::new(move |_| {
/// provide_context(String::from("foo"));
///
/// Effect::new(move |_| {
/// let value = with_context::<String, _>(|val| val.to_string())
/// .expect("could not find String in context");
/// assert_eq!(value, "foo");
/// });
/// });
/// # });
/// ```
pub fn with_context<T: 'static, R>(cb: impl FnOnce(&T) -> R) -> Option<R> {
Owner::current().and_then(|owner| owner.with_context(cb))
}
/// Update a context value of type `T` in the reactive system.
///
/// This traverses the reactive ownership graph, beginning from the current reactive
/// [`Owner`] and iterating through its parents, if any. When the value is found,
/// the function that you pass is applied to a mutable reference to it.
///
/// The context value should have been provided elsewhere using
/// [`provide_context`](provide_context).
///
/// This is useful for passing values down to components or functions lower in a
/// hierarchy without needs to “prop drill” by passing them through each layer as
/// arguments to a function or properties of a component.
///
/// Context works similarly to variable scope: a context that is provided higher in
/// the reactive graph can be used lower down, but a context that is provided lower
/// in the tree cannot be used higher up.
///
/// ```rust
/// # use reactive_graph::prelude::*;
/// # use reactive_graph::owner::*;
/// # let owner = Owner::new(); owner.set();
/// # use reactive_graph::effect::Effect;
/// # futures::executor::block_on(async move {
/// # any_spawner::Executor::init_futures_executor();
/// Effect::new(move |_| {
/// provide_context(String::from("foo"));
///
/// Effect::new(move |_| {
/// let value = update_context::<String, _>(|val| {
/// std::mem::replace(val, "bar".to_string())
/// })
/// .expect("could not find String in context");
/// assert_eq!(value, "foo");
/// assert_eq!(expect_context::<String>(), "bar");
/// });
/// });
/// # });
/// ```
pub fn update_context<T: 'static, R>(
cb: impl FnOnce(&mut T) -> R,
) -> Option<R> {
Owner::current().and_then(|owner| owner.update_context(cb))
}

View File

@@ -54,10 +54,6 @@ pub trait Storage<T>: Send + Sync + 'static {
/// Sets a new value for the stored value. If it has been disposed, returns `Some(T)`.
fn try_set(node: NodeId, value: T) -> Option<T>;
/// Takes an item from the arena if it exists and can be accessed from this thread.
/// If it cannot be casted, it will still be removed from the arena.
fn take(node: NodeId) -> Option<T>;
}
/// A form of [`Storage`] that stores the type as itself, with no wrapper.
@@ -104,16 +100,6 @@ where
}
})
}
fn take(node: NodeId) -> Option<T> {
Arena::with_mut(|arena| {
let m = arena.remove(node)?;
match m.downcast::<T>() {
Ok(inner) => Some(*inner),
Err(_) => None,
}
})
}
}
/// A form of [`Storage`] that stores the type with a wrapper that makes it `Send + Sync`, but only
@@ -162,14 +148,4 @@ where
}
})
}
fn take(node: NodeId) -> Option<T> {
Arena::with_mut(|arena| {
let m = arena.remove(node)?;
match m.downcast::<SendWrapper<T>>() {
Ok(inner) => Some(inner.take()),
Err(_) => None,
}
})
}
}

View File

@@ -4,9 +4,7 @@ use super::{
};
use crate::{
signal::guards::{Plain, ReadGuard, UntrackedWriteGuard},
traits::{
DefinedAt, Dispose, IntoInner, IsDisposed, ReadValue, WriteValue,
},
traits::{DefinedAt, Dispose, IsDisposed, ReadValue, WriteValue},
unwrap_signal,
};
use std::{
@@ -24,7 +22,7 @@ use std::{
/// updating it does not notify anything else.
pub struct StoredValue<T, S = SyncStorage> {
value: ArenaItem<ArcStoredValue<T>, S>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
@@ -64,11 +62,11 @@ impl<T, S> Hash for StoredValue<T, S> {
impl<T, S> DefinedAt for StoredValue<T, S> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -85,7 +83,7 @@ where
pub fn new_with_storage(value: T) -> Self {
Self {
value: ArenaItem::new_with_storage(ArcStoredValue::new(value)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
}
}
@@ -164,19 +162,6 @@ impl<T, S> Dispose for StoredValue<T, S> {
}
}
impl<T, S> IntoInner for StoredValue<T, S>
where
T: 'static,
S: Storage<ArcStoredValue<T>>,
{
type Value = T;
#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
self.value.into_inner()?.into_inner()
}
}
impl<T> From<ArcStoredValue<T>> for StoredValue<T>
where
T: Send + Sync + 'static,
@@ -184,7 +169,7 @@ where
#[track_caller]
fn from(value: ArcStoredValue<T>) -> Self {
StoredValue {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
value: ArenaItem::new(value),
}

View File

@@ -113,8 +113,7 @@ pub fn arc_signal<T>(value: T) -> (ArcReadSignal<T>, ArcWriteSignal<T>) {
pub fn signal<T: Send + Sync + 'static>(
value: T,
) -> (ReadSignal<T>, WriteSignal<T>) {
let (r, w) = arc_signal(value);
(r.into(), w.into())
RwSignal::new(value).split()
}
/// Creates an arena-allocated signal.

View File

@@ -4,7 +4,7 @@ use super::{
};
use crate::{
graph::SubscriberSet,
traits::{DefinedAt, IntoInner, IsDisposed, ReadUntracked},
traits::{DefinedAt, IsDisposed, ReadUntracked},
};
use core::fmt::{Debug, Formatter, Result};
use std::{
@@ -54,7 +54,7 @@ use std::{
/// assert_eq!(count.read(), 0);
/// ```
pub struct ArcReadSignal<T> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static Location<'static>,
pub(crate) value: Arc<RwLock<T>>,
pub(crate) inner: Arc<RwLock<SubscriberSet>>,
@@ -64,7 +64,7 @@ impl<T> Clone for ArcReadSignal<T> {
#[track_caller]
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
value: Arc::clone(&self.value),
inner: Arc::clone(&self.inner),
@@ -85,7 +85,7 @@ impl<T: Default> Default for ArcReadSignal<T> {
#[track_caller]
fn default() -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
value: Arc::new(RwLock::new(T::default())),
inner: Arc::new(RwLock::new(SubscriberSet::new())),
@@ -110,11 +110,11 @@ impl<T> Hash for ArcReadSignal<T> {
impl<T> DefinedAt for ArcReadSignal<T> {
#[inline(always)]
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -128,15 +128,6 @@ impl<T> IsDisposed for ArcReadSignal<T> {
}
}
impl<T> IntoInner for ArcReadSignal<T> {
type Value = T;
#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
Some(Arc::into_inner(self.value)?.into_inner().unwrap())
}
}
impl<T> AsSubscriberSet for ArcReadSignal<T> {
type Output = Arc<RwLock<SubscriberSet>>;

View File

@@ -6,7 +6,7 @@ use super::{
use crate::{
graph::{ReactiveNode, SubscriberSet},
prelude::{IsDisposed, Notify},
traits::{DefinedAt, IntoInner, ReadUntracked, UntrackableGuard, Write},
traits::{DefinedAt, ReadUntracked, UntrackableGuard, Write},
};
use core::fmt::{Debug, Formatter, Result};
use std::{
@@ -94,7 +94,7 @@ use std::{
/// assert_eq!(double_count(), 2);
/// ```
pub struct ArcRwSignal<T> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static Location<'static>,
pub(crate) value: Arc<RwLock<T>>,
pub(crate) inner: Arc<RwLock<SubscriberSet>>,
@@ -104,7 +104,7 @@ impl<T> Clone for ArcRwSignal<T> {
#[track_caller]
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
value: Arc::clone(&self.value),
inner: Arc::clone(&self.inner),
@@ -154,7 +154,7 @@ impl<T> ArcRwSignal<T> {
#[track_caller]
pub fn new(value: T) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
value: Arc::new(RwLock::new(value)),
inner: Arc::new(RwLock::new(SubscriberSet::new())),
@@ -165,7 +165,7 @@ impl<T> ArcRwSignal<T> {
#[track_caller]
pub fn read_only(&self) -> ArcReadSignal<T> {
ArcReadSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
value: Arc::clone(&self.value),
inner: Arc::clone(&self.inner),
@@ -176,7 +176,7 @@ impl<T> ArcRwSignal<T> {
#[track_caller]
pub fn write_only(&self) -> ArcWriteSignal<T> {
ArcWriteSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
value: Arc::clone(&self.value),
inner: Arc::clone(&self.inner),
@@ -198,7 +198,7 @@ impl<T> ArcRwSignal<T> {
) -> Option<Self> {
if Arc::ptr_eq(&read.inner, &write.inner) {
Some(Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
value: read.value,
inner: read.inner,
@@ -212,11 +212,11 @@ impl<T> ArcRwSignal<T> {
impl<T> DefinedAt for ArcRwSignal<T> {
#[inline(always)]
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -230,15 +230,6 @@ impl<T> IsDisposed for ArcRwSignal<T> {
}
}
impl<T> IntoInner for ArcRwSignal<T> {
type Value = T;
#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
Some(Arc::into_inner(self.value)?.into_inner().unwrap())
}
}
impl<T> AsSubscriberSet for ArcRwSignal<T> {
type Output = Arc<RwLock<SubscriberSet>>;

View File

@@ -13,7 +13,7 @@ use std::{
///
/// This can be useful for when using external data not stored in signals, for example.
pub struct ArcTrigger {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static Location<'static>,
pub(crate) inner: Arc<RwLock<SubscriberSet>>,
}
@@ -23,7 +23,7 @@ impl ArcTrigger {
#[track_caller]
pub fn new() -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: Default::default(),
}
@@ -40,7 +40,7 @@ impl Clone for ArcTrigger {
#[track_caller]
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
inner: Arc::clone(&self.inner),
}
@@ -72,11 +72,11 @@ impl AsSubscriberSet for ArcTrigger {
impl DefinedAt for ArcTrigger {
#[inline(always)]
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}

View File

@@ -2,7 +2,7 @@ use super::guards::{UntrackedWriteGuard, WriteGuard};
use crate::{
graph::{ReactiveNode, SubscriberSet},
prelude::{IsDisposed, Notify},
traits::{DefinedAt, IntoInner, UntrackableGuard, Write},
traits::{DefinedAt, UntrackableGuard, Write},
};
use core::fmt::{Debug, Formatter, Result};
use std::{
@@ -54,7 +54,7 @@ use std::{
/// assert_eq!(count.get(), 3);
/// ```
pub struct ArcWriteSignal<T> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static Location<'static>,
pub(crate) value: Arc<RwLock<T>>,
pub(crate) inner: Arc<RwLock<SubscriberSet>>,
@@ -64,7 +64,7 @@ impl<T> Clone for ArcWriteSignal<T> {
#[track_caller]
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
value: Arc::clone(&self.value),
inner: Arc::clone(&self.inner),
@@ -98,11 +98,11 @@ impl<T> Hash for ArcWriteSignal<T> {
impl<T> DefinedAt for ArcWriteSignal<T> {
#[inline(always)]
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -116,15 +116,6 @@ impl<T> IsDisposed for ArcWriteSignal<T> {
}
}
impl<T> IntoInner for ArcWriteSignal<T> {
type Value = T;
#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
Some(Arc::into_inner(self.value)?.into_inner().unwrap())
}
}
impl<T> Notify for ArcWriteSignal<T> {
fn notify(&self) {
self.inner.mark_dirty();

View File

@@ -104,7 +104,7 @@ impl<T: 'static> Debug for Plain<T> {
impl<T: 'static> Plain<T> {
/// Takes a reference-counted read guard on the given lock.
pub fn try_new(inner: Arc<RwLock<T>>) -> Option<Self> {
ArcRwLockReadGuardian::try_take(inner)?
ArcRwLockReadGuardian::take(inner)
.ok()
.map(|guard| Plain { guard })
}
@@ -331,7 +331,7 @@ pub struct UntrackedWriteGuard<T: 'static>(ArcRwLockWriteGuardian<T>);
impl<T: 'static> UntrackedWriteGuard<T> {
/// Creates a write guard from the given lock.
pub fn try_new(inner: Arc<RwLock<T>>) -> Option<Self> {
ArcRwLockWriteGuardian::try_take(inner)?
ArcRwLockWriteGuardian::take(inner)
.ok()
.map(UntrackedWriteGuard)
}

View File

@@ -6,7 +6,7 @@ use super::{
use crate::{
graph::SubscriberSet,
owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},
traits::{DefinedAt, Dispose, IntoInner, IsDisposed, ReadUntracked},
traits::{DefinedAt, Dispose, IsDisposed, ReadUntracked},
unwrap_signal,
};
use core::fmt::Debug;
@@ -58,7 +58,7 @@ use std::{
/// assert_eq!(count.read(), 0);
/// ```
pub struct ReadSignal<T, S = SyncStorage> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static Location<'static>,
pub(crate) inner: ArenaItem<ArcReadSignal<T>, S>,
}
@@ -105,11 +105,11 @@ impl<T, S> Hash for ReadSignal<T, S> {
impl<T, S> DefinedAt for ReadSignal<T, S> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -122,18 +122,6 @@ impl<T, S> IsDisposed for ReadSignal<T, S> {
}
}
impl<T, S> IntoInner for ReadSignal<T, S>
where
S: Storage<ArcReadSignal<T>>,
{
type Value = T;
#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
self.inner.into_inner()?.into_inner()
}
}
impl<T, S> AsSubscriberSet for ReadSignal<T, S>
where
S: Storage<ArcReadSignal<T>>,
@@ -168,7 +156,7 @@ where
#[track_caller]
fn from(value: ArcReadSignal<T>) -> Self {
ReadSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
@@ -182,7 +170,7 @@ where
#[track_caller]
fn from_local(value: ArcReadSignal<T>) -> Self {
ReadSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}

View File

@@ -8,7 +8,7 @@ use crate::{
owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},
signal::guards::{UntrackedWriteGuard, WriteGuard},
traits::{
DefinedAt, Dispose, IntoInner, IsDisposed, Notify, ReadUntracked,
DefinedAt, Dispose, IsDisposed, Notify, ReadUntracked,
UntrackableGuard, Write,
},
unwrap_signal,
@@ -100,7 +100,7 @@ use std::{
/// assert_eq!(double_count(), 2);
/// ```
pub struct RwSignal<T, S = SyncStorage> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
inner: ArenaItem<ArcRwSignal<T>, S>,
}
@@ -139,7 +139,7 @@ where
#[track_caller]
pub fn new_with_storage(value: T) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcRwSignal::new(value)),
}
@@ -172,7 +172,7 @@ where
#[track_caller]
pub fn read_only(&self) -> ReadSignal<T, S> {
ReadSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(
self.inner
@@ -194,7 +194,7 @@ where
#[track_caller]
pub fn write_only(&self) -> WriteSignal<T, S> {
WriteSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(
self.inner
@@ -231,10 +231,10 @@ where
(Some(read), Some(write)) => {
if Arc::ptr_eq(&read.inner, &write.inner) {
Some(Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcRwSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
value: Arc::clone(&read.value),
inner: Arc::clone(&read.inner),
@@ -296,11 +296,11 @@ impl<T, S> Hash for RwSignal<T, S> {
impl<T, S> DefinedAt for RwSignal<T, S> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -313,18 +313,6 @@ impl<T: 'static, S> IsDisposed for RwSignal<T, S> {
}
}
impl<T, S> IntoInner for RwSignal<T, S>
where
S: Storage<ArcRwSignal<T>>,
{
type Value = T;
#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
self.inner.into_inner()?.into_inner()
}
}
impl<T, S> AsSubscriberSet for RwSignal<T, S>
where
S: Storage<ArcRwSignal<T>>,
@@ -390,7 +378,7 @@ where
#[track_caller]
fn from(value: ArcRwSignal<T>) -> Self {
RwSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
@@ -414,7 +402,7 @@ where
#[track_caller]
fn from_local(value: ArcRwSignal<T>) -> Self {
RwSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}

View File

@@ -105,7 +105,7 @@ where
AnySource(
Arc::as_ptr(subs) as usize,
Arc::downgrade(subs) as Weak<dyn Source + Send + Sync>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
self.defined_at().expect("no DefinedAt in debug mode"),
)
})

View File

@@ -18,7 +18,7 @@ use std::{
/// [`Owner`](crate::owner::Owner) cleans up. For a reference-counted trigger that lives
/// as long as a reference to it is alive, see [`ArcTrigger`].
pub struct Trigger {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static Location<'static>,
pub(crate) inner: ArenaItem<ArcTrigger>,
}
@@ -28,7 +28,7 @@ impl Trigger {
#[track_caller]
pub fn new() -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new(ArcTrigger::new()),
}
@@ -83,11 +83,11 @@ impl AsSubscriberSet for Trigger {
impl DefinedAt for Trigger {
#[inline(always)]
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}

View File

@@ -1,10 +1,7 @@
use super::{guards::WriteGuard, ArcWriteSignal};
use crate::{
owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},
traits::{
DefinedAt, Dispose, IntoInner, IsDisposed, Notify, UntrackableGuard,
Write,
},
owner::{ArenaItem, Storage, SyncStorage},
traits::{DefinedAt, Dispose, IsDisposed, Notify, UntrackableGuard, Write},
};
use core::fmt::Debug;
use guardian::ArcRwLockWriteGuardian;
@@ -53,7 +50,7 @@ use std::{hash::Hash, ops::DerefMut, panic::Location, sync::Arc};
/// assert_eq!(count.get(), 3);
/// ```
pub struct WriteSignal<T, S = SyncStorage> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
pub(crate) defined_at: &'static Location<'static>,
pub(crate) inner: ArenaItem<ArcWriteSignal<T>, S>,
}
@@ -100,63 +97,23 @@ impl<T, S> Hash for WriteSignal<T, S> {
impl<T, S> DefinedAt for WriteSignal<T, S> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
}
}
impl<T> From<ArcWriteSignal<T>> for WriteSignal<T>
where
T: Send + Sync + 'static,
{
#[track_caller]
fn from(value: ArcWriteSignal<T>) -> Self {
WriteSignal {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
}
}
impl<T> FromLocal<ArcWriteSignal<T>> for WriteSignal<T, LocalStorage>
where
T: 'static,
{
#[track_caller]
fn from_local(value: ArcWriteSignal<T>) -> Self {
WriteSignal {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
}
}
impl<T, S> IsDisposed for WriteSignal<T, S> {
fn is_disposed(&self) -> bool {
self.inner.is_disposed()
}
}
impl<T, S> IntoInner for WriteSignal<T, S>
where
S: Storage<ArcWriteSignal<T>>,
{
type Value = T;
#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
self.inner.into_inner()?.into_inner()
}
}
impl<T, S> Notify for WriteSignal<T, S>
where
T: 'static,

View File

@@ -67,10 +67,10 @@ use std::{
#[macro_export]
macro_rules! unwrap_signal {
($signal:ident) => {{
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
let location = std::panic::Location::caller();
|| {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
panic!(
"{}",
@@ -80,7 +80,7 @@ macro_rules! unwrap_signal {
)
);
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
panic!(
"Tried to access a reactive value that has already been \
@@ -103,7 +103,6 @@ pub trait Dispose {
/// Allows tracking the value of some reactive data.
pub trait Track {
/// Subscribes to this signal in the current reactive scope without doing anything with its value.
#[track_caller]
fn track(&self);
}
@@ -405,7 +404,6 @@ where
/// Notifies subscribers of a change in this signal.
pub trait Notify {
/// Notifies subscribers of a change in this signal.
#[track_caller]
fn notify(&self);
}
@@ -632,18 +630,6 @@ pub trait IsDisposed {
fn is_disposed(&self) -> bool;
}
/// Turns a signal back into a raw value.
pub trait IntoInner {
/// The type of the value contained in the signal.
type Value;
/// Returns the inner value if this is the only reference to to the signal.
/// Otherwise, returns `None` and drops this reference.
/// # Panics
/// Panics if the inner lock is poisoned.
fn into_inner(self) -> Option<Self::Value>;
}
/// Describes where the signal was defined. This is used for diagnostic warnings and is purely a
/// debug-mode tool.
pub trait DefinedAt {

View File

@@ -93,35 +93,17 @@ pub mod read {
}
/// A wrapper for any kind of reference-counted reactive signal:
/// an [`ArcReadSignal`], [`ArcMemo`], [`ArcRwSignal`], or derived signal closure,
/// or a plain value of the same type
/// an [`ArcReadSignal`], [`ArcMemo`], [`ArcRwSignal`],
/// or derived signal closure.
///
/// This allows you to create APIs that take `T` or any reactive value that returns `T`
/// as an argument, rather than adding a generic `F: Fn() -> T`.
///
/// Values can be accessed with the same function call, `read()`, `with()`, and `get()`
/// APIs as other signals.
///
/// ## Important Notes about Derived Signals
///
/// `Signal::derive()` is simply a way to box and type-erase a “derived signal,” which
/// is a plain closure that accesses one or more signals. It does *not* cache the value
/// of that computation. Accessing the value of a `Signal<_>` that is created using `Signal::derive()`
/// will run the closure again every time you call `.read()`, `.with()`, or `.get()`.
///
/// If you want the closure to run the minimal number of times necessary to update its state,
/// and then to cache its value, you should use a [`Memo`] (and convert it into a `Signal<_>`)
/// rather than using `Signal::derive()`.
///
/// Note that for many computations, it is nevertheless less expensive to use a derived signal
/// than to create a separate memo and to cache the value: creating a new reactive node and
/// taking the lock on that cached value whenever you access the signal is *more* expensive than
/// simply re-running the calculation in many cases.
/// This allows you to create APIs that take any kind of `ArcSignal<T>` as an argument,
/// rather than adding a generic `F: Fn() -> T`. Values can be access with the same
/// function call, `with()`, and `get()` APIs as other signals.
pub struct ArcSignal<T: 'static, S = SyncStorage>
where
S: Storage<T>,
{
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
inner: SignalTypes<T, S>,
}
@@ -132,7 +114,7 @@ pub mod read {
{
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
inner: self.inner.clone(),
}
@@ -146,7 +128,7 @@ pub mod read {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut s = f.debug_struct("ArcSignal");
s.field("inner", &self.inner);
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
s.field("defined_at", &self.defined_at);
s.finish()
}
@@ -202,7 +184,7 @@ pub mod read {
Self {
inner: SignalTypes::DerivedSignal(Arc::new(derived_signal)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -212,7 +194,7 @@ pub mod read {
pub fn stored(value: T) -> Self {
Self {
inner: SignalTypes::Stored(ArcStoredValue::new(value)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -232,7 +214,7 @@ pub mod read {
fn from(value: ArcReadSignal<T>) -> Self {
Self {
inner: SignalTypes::ReadSignal(value),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -243,7 +225,7 @@ pub mod read {
fn from(value: ArcRwSignal<T>) -> Self {
Self {
inner: SignalTypes::ReadSignal(value.read_only()),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -257,7 +239,7 @@ pub mod read {
fn from(value: ArcMemo<T, S>) -> Self {
Self {
inner: SignalTypes::Memo(value),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -268,11 +250,11 @@ pub mod read {
S: Storage<T>,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -348,35 +330,16 @@ pub mod read {
}
/// A wrapper for any kind of arena-allocated reactive signal:
/// a [`ReadSignal`], [`Memo`], [`RwSignal`], or derived signal closure,
/// or a plain value of the same type
/// an [`ReadSignal`], [`Memo`], [`RwSignal`], or derived signal closure.
///
/// This allows you to create APIs that take `T` or any reactive value that returns `T`
/// as an argument, rather than adding a generic `F: Fn() -> T`.
///
/// Values can be accessed with the same function call, `read()`, `with()`, and `get()`
/// APIs as other signals.
///
/// ## Important Notes about Derived Signals
///
/// `Signal::derive()` is simply a way to box and type-erase a “derived signal,” which
/// is a plain closure that accesses one or more signals. It does *not* cache the value
/// of that computation. Accessing the value of a `Signal<_>` that is created using `Signal::derive()`
/// will run the closure again every time you call `.read()`, `.with()`, or `.get()`.
///
/// If you want the closure to run the minimal number of times necessary to update its state,
/// and then to cache its value, you should use a [`Memo`] (and convert it into a `Signal<_>`)
/// rather than using `Signal::derive()`.
///
/// Note that for many computations, it is nevertheless less expensive to use a derived signal
/// than to create a separate memo and to cache the value: creating a new reactive node and
/// taking the lock on that cached value whenever you access the signal is *more* expensive than
/// simply re-running the calculation in many cases.
/// This allows you to create APIs that take any kind of `Signal<T>` as an argument,
/// rather than adding a generic `F: Fn() -> T`. Values can be access with the same
/// function call, `with()`, and `get()` APIs as other signals.
pub struct Signal<T, S = SyncStorage>
where
S: Storage<T>,
{
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
inner: ArenaItem<SignalTypes<T, S>, S>,
}
@@ -408,7 +371,7 @@ pub mod read {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut s = f.debug_struct("Signal");
s.field("inner", &self.inner);
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
s.field("defined_at", &self.defined_at);
s.finish()
}
@@ -430,11 +393,11 @@ pub mod read {
S: Storage<T>,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -567,7 +530,7 @@ pub mod read {
inner: ArenaItem::new_with_storage(SignalTypes::DerivedSignal(
Arc::new(derived_signal),
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -579,7 +542,7 @@ pub mod read {
inner: ArenaItem::new_with_storage(SignalTypes::Stored(
ArcStoredValue::new(value),
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -606,7 +569,7 @@ pub mod read {
inner: ArenaItem::new_local(SignalTypes::DerivedSignal(
Arc::new(derived_signal),
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -619,7 +582,7 @@ pub mod read {
inner: ArenaItem::new_local(SignalTypes::Stored(
ArcStoredValue::new(value),
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -677,7 +640,7 @@ pub mod read {
#[track_caller]
fn from(value: ArcSignal<T, SyncStorage>) -> Self {
Signal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new(value.inner),
}
@@ -691,7 +654,7 @@ pub mod read {
#[track_caller]
fn from_local(value: ArcSignal<T, LocalStorage>) -> Self {
Signal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_local(value.inner),
}
@@ -705,7 +668,7 @@ pub mod read {
#[track_caller]
fn from(value: Signal<T, S>) -> Self {
ArcSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: value
.inner
@@ -723,7 +686,7 @@ pub mod read {
fn from(value: ReadSignal<T>) -> Self {
Self {
inner: ArenaItem::new(SignalTypes::ReadSignal(value.into())),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -739,7 +702,7 @@ pub mod read {
inner: ArenaItem::new_local(SignalTypes::ReadSignal(
value.into(),
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -753,7 +716,7 @@ pub mod read {
fn from(value: ArcReadSignal<T>) -> Self {
Self {
inner: ArenaItem::new(SignalTypes::ReadSignal(value)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -767,7 +730,7 @@ pub mod read {
fn from(value: ArcReadSignal<T>) -> Self {
Self {
inner: ArenaItem::new_local(SignalTypes::ReadSignal(value)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -783,7 +746,7 @@ pub mod read {
inner: ArenaItem::new(SignalTypes::ReadSignal(
value.read_only().into(),
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -799,7 +762,7 @@ pub mod read {
inner: ArenaItem::new_local(SignalTypes::ReadSignal(
value.read_only().into(),
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -815,7 +778,7 @@ pub mod read {
inner: ArenaItem::new(SignalTypes::ReadSignal(
value.read_only(),
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -831,7 +794,7 @@ pub mod read {
inner: ArenaItem::new_local(SignalTypes::ReadSignal(
value.read_only(),
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -845,7 +808,7 @@ pub mod read {
fn from(value: Memo<T>) -> Self {
Self {
inner: ArenaItem::new(SignalTypes::Memo(value.into())),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -859,7 +822,7 @@ pub mod read {
fn from(value: Memo<T, LocalStorage>) -> Self {
Self {
inner: ArenaItem::new_local(SignalTypes::Memo(value.into())),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -873,7 +836,7 @@ pub mod read {
fn from(value: ArcMemo<T>) -> Self {
Self {
inner: ArenaItem::new(SignalTypes::Memo(value)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -887,7 +850,7 @@ pub mod read {
fn from(value: ArcMemo<T, LocalStorage>) -> Self {
Self {
inner: ArenaItem::new_local(SignalTypes::Memo(value)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -1868,7 +1831,7 @@ pub mod write {
T: 'static,
{
inner: SignalSetterTypes<T, S>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static std::panic::Location<'static>,
}
@@ -1883,7 +1846,7 @@ pub mod write {
fn default() -> Self {
Self {
inner: SignalSetterTypes::Default,
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -1936,7 +1899,7 @@ pub mod write {
inner: SignalSetterTypes::Mapped(ArenaItem::new_with_storage(
Box::new(mapped_setter),
)),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -1947,7 +1910,7 @@ pub mod write {
fn from(value: WriteSignal<T, S>) -> Self {
Self {
inner: SignalSetterTypes::Write(value),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
@@ -1962,7 +1925,7 @@ pub mod write {
fn from(value: RwSignal<T, S>) -> Self {
Self {
inner: SignalSetterTypes::Write(value.write_only()),
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}

View File

@@ -443,43 +443,3 @@ fn unsync_derived_signal_and_memo() {
assert_eq!(f.with(|n| *n), 6);
assert_eq!(f.get_untracked(), 6);
}
#[test]
fn memo_updates_even_if_not_read_until_later() {
#![allow(clippy::bool_assert_comparison)]
let owner = Owner::new();
owner.set();
// regression test for https://github.com/leptos-rs/leptos/issues/3339
let input = RwSignal::new(0);
let first_memo = Memo::new(move |_| input.get() == 1);
let second_memo = Memo::new(move |_| first_memo.get());
assert_eq!(input.get(), 0);
assert_eq!(first_memo.get(), false);
println!("update to 1");
input.set(1);
assert_eq!(input.get(), 1);
println!("read memo 1");
assert_eq!(first_memo.get(), true);
println!("read memo 2");
assert_eq!(second_memo.get(), true);
// this time, we don't read the memo
println!("\nupdate to 2");
input.set(2);
assert_eq!(input.get(), 2);
println!("read memo 1");
assert_eq!(first_memo.get(), false);
println!("\nupdate to 3");
input.set(3);
assert_eq!(input.get(), 3);
println!("read memo 1");
assert_eq!(first_memo.get(), false);
println!("read memo 2");
assert_eq!(second_memo.get(), false);
}

View File

@@ -2,8 +2,8 @@ use reactive_graph::{
owner::Owner,
signal::{arc_signal, signal, ArcRwSignal, RwSignal},
traits::{
Dispose, Get, GetUntracked, IntoInner, Read, Set, Update,
UpdateUntracked, With, WithUntracked, Write,
Get, GetUntracked, Read, Set, Update, UpdateUntracked, With,
WithUntracked, Write,
},
};
@@ -108,35 +108,3 @@ fn update_signal() {
set_a.set(4);
assert_eq!(a.get(), 4);
}
#[test]
fn into_inner_signal() {
let owner = Owner::new();
owner.set();
let rw_signal = RwSignal::new(1);
assert_eq!(rw_signal.get(), 1);
assert_eq!(rw_signal.into_inner(), Some(1));
}
#[test]
fn into_inner_arc_signal() {
let owner = Owner::new();
owner.set();
let (a, b) = arc_signal(2);
assert_eq!(a.get(), 2);
std::mem::drop(b);
assert_eq!(a.into_inner(), Some(2));
}
#[test]
fn into_inner_non_arc_signal() {
let owner = Owner::new();
owner.set();
let (a, b) = signal(2);
assert_eq!(a.get(), 2);
b.dispose();
assert_eq!(a.into_inner(), Some(2));
}

View File

@@ -1,6 +1,6 @@
[package]
name = "reactive_stores"
version = "0.1.3"
version = "0.1.0-rc2"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"
@@ -23,6 +23,3 @@ tokio = { version = "1.41", features = ["rt-multi-thread", "macros"] }
tokio-test = { version = "0.4.4" }
any_spawner = { workspace = true, features = ["futures-executor", "tokio"] }
reactive_graph = { workspace = true, features = ["effects"] }
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(leptos_debuginfo)'] }

View File

@@ -26,7 +26,7 @@ pub struct ArcField<T>
where
T: 'static,
{
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
path: StorePath,
trigger: StoreFieldTrigger,
@@ -116,7 +116,7 @@ where
#[track_caller]
fn from(value: Store<T, S>) -> Self {
ArcField {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
path: value.path().into_iter().collect(),
trigger: value.get_trigger(value.path().into_iter().collect()),
@@ -136,7 +136,7 @@ where
#[track_caller]
fn from(value: ArcStore<T>) -> Self {
ArcField {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
path: value.path().into_iter().collect(),
trigger: value.get_trigger(value.path().into_iter().collect()),
@@ -174,7 +174,7 @@ where
#[track_caller]
fn from(value: Subfield<Inner, Prev, T>) -> Self {
ArcField {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
path: value.path().into_iter().collect(),
trigger: value.get_trigger(value.path().into_iter().collect()),
@@ -212,7 +212,7 @@ where
#[track_caller]
fn from(value: AtIndex<Inner, Prev>) -> Self {
ArcField {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
path: value.path().into_iter().collect(),
trigger: value.get_trigger(value.path().into_iter().collect()),
@@ -254,7 +254,7 @@ where
#[track_caller]
fn from(value: AtKeyed<Inner, Prev, K, T>) -> Self {
ArcField {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
path: value.path().into_iter().collect(),
trigger: value.get_trigger(value.path().into_iter().collect()),
@@ -285,7 +285,7 @@ where
impl<T> Clone for ArcField<T> {
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
path: self.path.clone(),
trigger: self.trigger.clone(),
@@ -300,11 +300,11 @@ impl<T> Clone for ArcField<T> {
impl<T> DefinedAt for ArcField<T> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}

View File

@@ -27,7 +27,7 @@ pub struct Field<T, S = SyncStorage>
where
T: 'static,
{
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
inner: ArenaItem<ArcField<T>, S>,
}
@@ -75,7 +75,7 @@ where
#[track_caller]
fn from(value: Store<T, S>) -> Self {
Field {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value.into()),
}
@@ -90,7 +90,7 @@ where
#[track_caller]
fn from(value: ArcStore<T>) -> Self {
Field {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value.into()),
}
@@ -108,7 +108,7 @@ where
#[track_caller]
fn from(value: Subfield<Inner, Prev, T>) -> Self {
Field {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value.into()),
}
@@ -126,7 +126,7 @@ where
#[track_caller]
fn from(value: AtIndex<Inner, Prev>) -> Self {
Field {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value.into()),
}
@@ -149,7 +149,7 @@ where
#[track_caller]
fn from(value: AtKeyed<Inner, Prev, K, T>) -> Self {
Field {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value.into()),
}
@@ -166,11 +166,11 @@ impl<T, S> Copy for Field<T, S> {}
impl<T, S> DefinedAt for Field<T, S> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}

View File

@@ -23,7 +23,7 @@ use std::{
/// Provides access to the data at some index in another collection.
#[derive(Debug)]
pub struct AtIndex<Inner, Prev> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
inner: Inner,
index: usize,
@@ -36,7 +36,7 @@ where
{
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
inner: self.inner.clone(),
index: self.index,
@@ -52,7 +52,7 @@ impl<Inner, Prev> AtIndex<Inner, Prev> {
#[track_caller]
pub fn new(inner: Inner, index: usize) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner,
index,
@@ -115,11 +115,11 @@ where
Inner: StoreField<Value = Prev>,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}

View File

@@ -28,7 +28,7 @@ pub struct KeyedSubfield<Inner, Prev, K, T>
where
for<'a> &'a T: IntoIterator,
{
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
path_segment: StorePathSegment,
inner: Inner,
@@ -44,7 +44,7 @@ where
{
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
path_segment: self.path_segment,
inner: self.inner.clone(),
@@ -76,7 +76,7 @@ where
write: fn(&mut Prev) -> &mut T,
) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner,
path_segment,
@@ -254,11 +254,11 @@ where
Inner: StoreField<Value = Prev>,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -356,7 +356,7 @@ pub struct AtKeyed<Inner, Prev, K, T>
where
for<'a> &'a T: IntoIterator,
{
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
inner: KeyedSubfield<Inner, Prev, K, T>,
key: K,
@@ -370,7 +370,7 @@ where
{
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
inner: self.inner.clone(),
key: self.key.clone(),
@@ -394,7 +394,7 @@ where
#[track_caller]
pub fn new(inner: KeyedSubfield<Inner, Prev, K, T>, key: K) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner,
key,
@@ -450,7 +450,10 @@ where
let inner = self.inner.reader()?;
let inner_path = self.inner.path().into_iter().collect();
let keys = self.inner.keys()?;
let keys = self
.inner
.keys()
.expect("using keys on a store with no keys");
let index = keys
.with_field_keys(
inner_path,
@@ -458,7 +461,8 @@ where
|| self.inner.latest_keys(),
)
.flatten()
.map(|(_, idx)| idx)?;
.map(|(_, idx)| idx)
.expect("reading from a keyed field that has not yet been created");
Some(MappedMutArc::new(
inner,
@@ -507,11 +511,11 @@ where
for<'a> &'a T: IntoIterator,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -619,15 +623,10 @@ where
.keys()
.expect("updating keys on a store with no keys");
// generating the latest keys out here means that if we have
// nested keyed fields, the second field will not try to take a
// read-lock on the key map to get the field while the first field
// is still holding the write-lock in the closure below
let latest = self.latest_keys();
keys.with_field_keys(
inner_path,
|keys| {
keys.update(latest);
keys.update(self.latest_keys());
},
|| self.latest_keys(),
);
@@ -650,8 +649,8 @@ where
#[track_caller]
fn into_iter(self) -> StoreFieldKeyedIter<Inner, Prev, K, T> {
// reactively track changes to this field
self.update_keys();
self.track_field();
let trigger = self.get_trigger(self.path().into_iter().collect());
trigger.this.track();
// get the current length of the field by accessing slice
let reader = self

View File

@@ -114,8 +114,8 @@ use reactive_graph::{
ArcTrigger,
},
traits::{
DefinedAt, Dispose, IsDisposed, Notify, ReadUntracked, Track,
UntrackableGuard, Write,
DefinedAt, IsDisposed, Notify, ReadUntracked, Track, UntrackableGuard,
Write,
},
};
pub use reactive_stores_macro::{Patch, Store};
@@ -315,7 +315,7 @@ impl KeyMap {
/// This adds a getter method for each field to `Store<T>`, which allow accessing reactive versions
/// of each individual field of the struct.
pub struct ArcStore<T> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
pub(crate) value: Arc<RwLock<T>>,
signals: Arc<RwLock<TriggerMap>>,
@@ -326,7 +326,7 @@ impl<T> ArcStore<T> {
/// Creates a new store from the initial value.
pub fn new(value: T) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
value: Arc::new(RwLock::new(value)),
signals: Default::default(),
@@ -335,16 +335,10 @@ impl<T> ArcStore<T> {
}
}
impl<T: Default> Default for ArcStore<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: Debug> Debug for ArcStore<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut f = f.debug_struct("ArcStore");
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
let f = f.field("defined_at", &self.defined_at);
f.field("value", &self.value)
.field("signals", &self.signals)
@@ -355,7 +349,7 @@ impl<T: Debug> Debug for ArcStore<T> {
impl<T> Clone for ArcStore<T> {
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
value: Arc::clone(&self.value),
signals: Arc::clone(&self.signals),
@@ -366,11 +360,11 @@ impl<T> Clone for ArcStore<T> {
impl<T> DefinedAt for ArcStore<T> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -439,7 +433,7 @@ impl<T: 'static> Notify for ArcStore<T> {
/// This follows the same ownership rules as arena-allocated types like
/// [`RwSignal`](reactive_graph::signal::RwSignal).
pub struct Store<T, S = SyncStorage> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
inner: ArenaItem<ArcStore<T>, S>,
}
@@ -451,7 +445,7 @@ where
/// Creates a new store with the initial value.
pub fn new(value: T) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcStore::new(value)),
}
@@ -467,38 +461,20 @@ where
/// This pins the value to the current thread. Accessing it from any other thread will panic.
pub fn new_local(value: T) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcStore::new(value)),
}
}
}
impl<T> Default for Store<T>
where
T: Default + Send + Sync + 'static,
{
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> Default for Store<T, LocalStorage>
where
T: Default + 'static,
{
fn default() -> Self {
Self::new_local(T::default())
}
}
impl<T: Debug, S> Debug for Store<T, S>
where
S: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut f = f.debug_struct("Store");
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
let f = f.field("defined_at", &self.defined_at);
f.field("inner", &self.inner).finish()
}
@@ -514,11 +490,11 @@ impl<T, S> Copy for Store<T, S> {}
impl<T, S> DefinedAt for Store<T, S> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -535,15 +511,6 @@ where
}
}
impl<T, S> Dispose for Store<T, S>
where
T: 'static,
{
fn dispose(self) {
self.inner.dispose();
}
}
impl<T, S> ReadUntracked for Store<T, S>
where
T: 'static,
@@ -602,20 +569,6 @@ where
}
}
impl<T, S> From<ArcStore<T>> for Store<T, S>
where
T: 'static,
S: Storage<ArcStore<T>>,
{
fn from(value: ArcStore<T>) -> Self {
Self {
#[cfg(debug_assertions)]
defined_at: value.defined_at,
inner: ArenaItem::new_with_storage(value),
}
}
}
#[cfg(test)]
mod tests {
use crate::{self as reactive_stores, Patch, Store, StoreFieldIterator};

View File

@@ -78,7 +78,6 @@ patch_primitives! {
Arc<str>,
Rc<str>,
Cow<'_, str>,
usize,
u8,
u16,
u32,

View File

@@ -24,15 +24,12 @@ pub trait StoreField: Sized {
type Writer: UntrackableGuard<Target = Self::Value>;
/// Returns the trigger that tracks access and updates for this field.
#[track_caller]
fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger;
/// The path of this field (see [`StorePath`]).
#[track_caller]
fn path(&self) -> impl IntoIterator<Item = StorePathSegment>;
/// Reactively tracks this field.
#[track_caller]
fn track_field(&self) {
let path = self.path().into_iter().collect();
let trigger = self.get_trigger(path);
@@ -41,15 +38,12 @@ pub trait StoreField: Sized {
}
/// Returns a read guard to access this field.
#[track_caller]
fn reader(&self) -> Option<Self::Reader>;
/// Returns a write guard to update this field.
#[track_caller]
fn writer(&self) -> Option<Self::Writer>;
/// The keys for this field, if it is a keyed field.
#[track_caller]
fn keys(&self) -> Option<KeyMap>;
}
@@ -61,31 +55,26 @@ where
type Reader = Plain<T>;
type Writer = WriteGuard<ArcTrigger, UntrackedWriteGuard<T>>;
#[track_caller]
fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {
let triggers = &self.signals;
let trigger = triggers.write().or_poisoned().get_or_insert(path);
trigger
}
#[track_caller]
fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
iter::empty()
}
#[track_caller]
fn reader(&self) -> Option<Self::Reader> {
Plain::try_new(Arc::clone(&self.value))
}
#[track_caller]
fn writer(&self) -> Option<Self::Writer> {
let trigger = self.get_trigger(Default::default());
let guard = UntrackedWriteGuard::try_new(Arc::clone(&self.value))?;
Some(WriteGuard::new(trigger.children, guard))
}
#[track_caller]
fn keys(&self) -> Option<KeyMap> {
Some(self.keys.clone())
}
@@ -100,7 +89,6 @@ where
type Reader = Plain<T>;
type Writer = WriteGuard<ArcTrigger, UntrackedWriteGuard<T>>;
#[track_caller]
fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {
self.inner
.try_get_value()
@@ -108,7 +96,6 @@ where
.unwrap_or_else(unwrap_signal!(self))
}
#[track_caller]
fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
self.inner
.try_get_value()
@@ -116,17 +103,14 @@ where
.unwrap_or_else(unwrap_signal!(self))
}
#[track_caller]
fn reader(&self) -> Option<Self::Reader> {
self.inner.try_get_value().and_then(|n| n.reader())
}
#[track_caller]
fn writer(&self) -> Option<Self::Writer> {
self.inner.try_get_value().and_then(|n| n.writer())
}
#[track_caller]
fn keys(&self) -> Option<KeyMap> {
self.inner.try_get_value().and_then(|inner| inner.keys())
}

View File

@@ -18,7 +18,7 @@ use std::{iter, marker::PhantomData, ops::DerefMut, panic::Location};
/// Accesses a single field of a reactive structure.
#[derive(Debug)]
pub struct Subfield<Inner, Prev, T> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
path_segment: StorePathSegment,
inner: Inner,
@@ -33,7 +33,7 @@ where
{
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: self.defined_at,
path_segment: self.path_segment,
inner: self.inner.clone(),
@@ -56,7 +56,7 @@ impl<Inner, Prev, T> Subfield<Inner, Prev, T> {
write: fn(&mut Prev) -> &mut T,
) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner,
path_segment,
@@ -103,7 +103,6 @@ where
self.inner.keys()
}
#[track_caller]
fn track_field(&self) {
let inner = self
.inner
@@ -120,11 +119,11 @@ where
Inner: StoreField<Value = Prev>,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
#[cfg(not(debug_assertions))]
{
None
}
@@ -145,7 +144,6 @@ where
Inner: StoreField<Value = Prev>,
Prev: 'static,
{
#[track_caller]
fn notify(&self) {
let trigger = self.get_trigger(self.path().into_iter().collect());
trigger.this.notify();
@@ -159,7 +157,6 @@ where
Prev: 'static,
T: 'static,
{
#[track_caller]
fn track(&self) {
self.track_field();
}

View File

@@ -1,6 +1,6 @@
[package]
name = "reactive_stores_macro"
version = "0.1.0"
version = "0.1.0-rc2"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos_router"
version = "0.7.4"
version = "0.7.0-rc2"
authors = ["Greg Johnston", "Ben Wishovich"]
license = "MIT"
readme = "../README.md"
@@ -19,9 +19,9 @@ reactive_graph = { workspace = true }
tachys = { workspace = true, features = ["reactive_graph"] }
futures = "0.3.31"
url = "2.5"
js-sys = { version = "0.3.74" }
wasm-bindgen = { version = "0.2.97" }
tracing = { version = "0.1.41", optional = true }
js-sys = { version = "0.3.72" }
wasm-bindgen = { version = "0.2.95" }
tracing = { version = "0.1.40", optional = true }
once_cell = "1.20"
send_wrapper = "0.6.0"
thiserror = "2.0"

View File

@@ -32,7 +32,7 @@ use tachys::view::any_view::AnyView;
/// A wrapper that allows passing route definitions as children to a component like [`Routes`],
/// [`FlatRoutes`], [`ParentRoute`], or [`ProtectedParentRoute`].
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct RouteChildren<Children>(Children);
impl<Children> RouteChildren<Children> {
@@ -82,18 +82,13 @@ where
#[cfg(not(feature = "ssr"))]
let (location_provider, current_url, redirect_hook) = {
let owner = Owner::current();
let location =
BrowserUrl::new().expect("could not access browser navigation"); // TODO options here
location.init(base.clone());
provide_context(location.clone());
let current_url = location.as_url().clone();
let redirect_hook = Box::new(move |loc: &str| {
if let Some(owner) = &owner {
owner.with(|| BrowserUrl::redirect(loc));
}
});
let redirect_hook = Box::new(|loc: &str| BrowserUrl::redirect(loc));
(Some(location), current_url, redirect_hook)
};

View File

@@ -92,9 +92,6 @@ pub fn A<H>(
/// a trailing slash.
#[prop(optional)]
strict_trailing_slash: bool,
/// If `true`, the router will scroll to the top of the window at the end of navigation. Defaults to `true`.
#[prop(default = true)]
scroll: bool,
/// The nodes or elements to be shown inside the link.
children: Children,
) -> impl IntoView
@@ -107,7 +104,6 @@ where
exact: bool,
children: Children,
strict_trailing_slash: bool,
scroll: bool,
) -> impl IntoView {
let RouterContext { current_url, .. } =
use_context().expect("tried to use <A/> outside a <Router/>.");
@@ -133,7 +129,6 @@ where
href=move || href.get().unwrap_or_default()
target=target
aria-current=move || if is_active() { Some("page") } else { None }
data-noscroll=!scroll
>
{children()}
@@ -142,7 +137,7 @@ where
}
let href = use_resolved_path(move || href.to_href()());
inner(href, target, exact, children, strict_trailing_slash, scroll)
inner(href, target, exact, children, strict_trailing_slash)
}
// Test if `href` is active for `location`. Assumes _both_ `href` and `location` begin with a `'/'`.

View File

@@ -359,8 +359,7 @@ where
let change = LocationChange {
value: to,
replace,
scroll: !a.has_attribute("noscroll")
&& !a.has_attribute("data-noscroll"),
scroll: true,
state: State::new(state),
};

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos_router_macro"
version = "0.7.4"
version = "0.7.0-rc2"
authors = ["Greg Johnston", "Ben Wishovich"]
license = "MIT"
readme = "../README.md"
@@ -19,6 +19,3 @@ quote = "1.0"
[dev-dependencies]
leptos_router = { path = "../router" }
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(leptos_debuginfo)'] }

8
server_fn/Cargo.lock generated
View File

@@ -1190,7 +1190,7 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.7.2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
@@ -1447,7 +1447,7 @@ dependencies = [
[[package]]
name = "radium"
version = "0.7.2"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
@@ -1704,7 +1704,7 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sct"
version = "0.7.2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
@@ -1810,7 +1810,7 @@ dependencies = [
[[package]]
name = "serde_urlencoded"
version = "0.7.2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [

View File

@@ -49,16 +49,16 @@ http = { version = "1.1" }
ciborium = { version = "0.2.2", optional = true }
postcard = { version = "1", features = ["alloc"], optional = true }
hyper = { version = "1.5", optional = true }
bytes = "1.9"
bytes = "1.8"
http-body-util = { version = "0.1.2", optional = true }
rkyv = { version = "0.8.9", optional = true }
rkyv = { version = "0.8.8", optional = true }
rmp-serde = { version = "1.3.0", optional = true }
# client
gloo-net = { version = "0.6.0", optional = true }
js-sys = { version = "0.3.74", optional = true }
wasm-bindgen = { version = "0.2.97", optional = true }
wasm-bindgen-futures = { version = "0.4.47", optional = true }
js-sys = { version = "0.3.72", optional = true }
wasm-bindgen = { version = "0.2.95", optional = true }
wasm-bindgen-futures = { version = "0.4.45", optional = true }
wasm-streams = { version = "0.4.2", optional = true }
web-sys = { version = "0.3.72", optional = true, features = [
"console",
@@ -229,6 +229,3 @@ skip_feature_sets = [
"rkyv",
],
]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(leptos_debuginfo)'] }

Some files were not shown because too many files have changed in this diff Show More