Compare commits

..

209 Commits

Author SHA1 Message Date
Greg Johnston
2ee4444bb4 v0.8.2 2025-05-06 14:09:35 -04:00
Luxalpa
03a1c1e7a6 fix: ensure unique style caching hashes (#3947) 2025-05-06 14:00:29 -04:00
Greg Johnston
12e49ed996 Merge pull request #3950 from leptos-rs/3945
fix: correct order of meta content relative to surrounding tags (closes #3945)
2025-05-06 13:59:28 -04:00
Greg Johnston
1e281e9e74 fix(examples): bugfix revealed a pre-existing bug with meta tags in the hackernews demo! 2025-05-06 11:49:43 -04:00
Greg Johnston
bd475f89d0 fix: correct order of meta content relative to surrounding tags (closes #3945) 2025-05-06 11:19:19 -04:00
Greg Johnston
3d91b5e90f v0.8.1 2025-05-05 21:39:43 -04:00
Greg Johnston
96d8d5218c Merge pull request #3942 from leptos-rs/3907
Some `islands_router` improvements
2025-05-05 21:33:57 -04:00
Greg Johnston
84caa35cef feat: add .map() and .and_then() on LocalResource (#3941) 2025-05-05 21:20:34 -04:00
Greg Johnston
fc8b55161c fix: remove extra marker node after text node when marking a branch (closes #3936) (#3940) 2025-05-05 21:20:16 -04:00
Greg Johnston
657052466b fix: use a runtime check rather than an unnecessary Either to determine how to render islands (see #3896; closes #3929) (#3938) 2025-05-05 19:41:29 -04:00
william light
efe8336363 reactive_stores: implement PartialEq and Eq for Store (#3915)
StoredValue also has these implemented and does the same thing.
2025-05-05 14:32:42 -04:00
Greg Johnston
770881842c fix: correctly provide context through islands to children (closes #3928) (#3933) 2025-05-05 13:00:40 -04:00
Greg Johnston
0d540ef02f fix: ensure that nested children of a RenderEffect are dropped while dropped a RenderEffect (closes #3922) (#3926) 2025-05-05 13:00:20 -04:00
Saber Haj Rabiee
dc1885ad92 feat: check the counter_isomorphic release build with the leptos_debuginfo flag (#3918) 2025-05-04 15:22:04 -04:00
Eric Roman
61bf87439a Fix some typos in the documentation/examples for reactive store. (#3924) 2025-05-03 20:50:13 -04:00
Greg Johnston
308568e520 fix(CI): prevent regreession from nightly clippy in autofix (#3917)
* fix(CI): prevent regreession from nightly clippy in autofix

* chore: format

* chore: update nightly to 2025-04-16 (proc-macro span, #3852)

* chore: improve the autofix ci workflow

* fix: adjust ServerFn macro test stderr based on nightly-2025-04-16

* fix: limit server_fn server macro trybuild tests nightly only
2025-05-03 20:47:42 -04:00
Greg Johnston
1b0f32dc4c fix: clear and re-throw errors in correct order (#3923) 2025-05-03 20:46:57 -04:00
Greg Johnston
2e0b3011d9 fix: correct issues with StaticVec::rebuild() by aligning implementation with Vec::rebuild() (closes #3906) (#3920) 2025-05-03 08:56:36 -04:00
Greg Johnston
680d4ccd07 fix: do not diff islands in islands_router mode (see #3907) 2025-05-02 21:20:16 -04:00
Greg Johnston
325f9cbe33 fix: don't handle ActionForm (et al) with the islands_router default <form> behavior (see #3907) 2025-05-02 21:12:20 -04:00
Greg Johnston
26ab392c95 fix: allow nested Suspense > ErrorBoundary > Suspense (closes #3908) (#3913) 2025-05-02 16:59:04 -04:00
Saber Haj Rabiee
3a4e2a19aa fix: limit server_fn server macro trybuild tests nightly only 2025-05-02 08:32:38 -07:00
Saber Haj Rabiee
eed3d21b40 fix: adjust ServerFn macro test stderr based on nightly-2025-04-16 2025-05-02 08:01:15 -07:00
Saber Haj Rabiee
7ae386285d chore: improve the autofix ci workflow 2025-05-02 07:27:45 -07:00
Saber Haj Rabiee
ebcc51136d chore: update nightly to 2025-04-16 (proc-macro span, #3852) 2025-05-02 07:13:34 -07:00
Saber Haj Rabiee
e10ded4fd0 chore: format 2025-05-02 06:58:01 -07:00
Saber Haj Rabiee
67be872f58 fix(CI): prevent regreession from nightly clippy in autofix 2025-05-02 06:31:39 -07:00
LeoniePhiline
e5b21ac0fc fix(docs): correct panic message in copied example code (#3911) 2025-05-02 08:14:36 -04:00
benwis
9b2e313d20 v0.8.0 2025-05-01 15:26:21 -07:00
Greg Johnston
bc79232033 feat: impl From<MappedSignal<T>> for Signal<T> (#3897) 2025-05-01 14:56:27 -04:00
Saber Haj Rabiee
a7bb2565c4 fix(examples): websocket tests fail (occasionally) second attemp (#3910)
do not check the label immediately
2025-05-01 14:54:57 -04:00
zakstucke
2e393aaca0 fix: leptos_debuginfo (#3899) 2025-05-01 14:54:24 -04:00
Greg Johnston
e37711cb85 fix: correct hydration for elements after island children (closes #3904) (#3905) 2025-05-01 09:44:40 -04:00
Greg Johnston
627b553e60 fix: prevent sibling context leakage in islands (closes #3902) (#3903) 2025-04-30 21:33:54 -04:00
Greg Johnston
e2f9aca466 Merge pull request #3901 from leptos-rs/3896
Fix some island-routing issues
2025-04-30 14:27:31 -04:00
Greg Johnston
970544ed0b fix: correctly hydrate branches inside islands when using islands-router (closes #3896) 2025-04-30 08:48:40 -04:00
Greg Johnston
0c3b3c440f chore: remove forgotten log 2025-04-30 08:36:23 -04:00
nickburlett
6578086e09 fix(examples): incorrect routes in hackernews example (closes #3892) (#3894)
* fix(examples): incorrect routes in hackernews example (closes #3892)

1. `Avoid calling category()` twice on the story type.
2. `get_static_file()` returns Err on not found, so don’t
   unconditionally `unwrap()` it

* cargo fmt

---------

Co-authored-by: Greg Johnston <greg.johnston@gmail.com>
2025-04-30 07:43:12 -04:00
Greg Johnston
113aba9666 docs: add note about file hashing in Stylesheet docs (#3898) 2025-04-29 19:03:18 -07:00
Greg Johnston
58d7475193 fix: don't render branching comments inside script/style tags 2025-04-29 20:32:56 -04:00
Greg Johnston
04d80ff8d0 chore: clippy 2025-04-29 20:31:44 -04:00
Greg Johnston
7971a2dccb fix: marking AnyView branches with out-of-order streaming (for islands_router) 2025-04-29 20:30:40 -04:00
Greg Johnston
e2ea4277bc fix(examples): broken favicons in hackernews examples (closes #3890) (#3891) 2025-04-28 09:52:15 -04:00
Greg Johnston
171c8e7ff7 v0.8.0-rc3 2025-04-26 15:53:20 -04:00
Greg Johnston
53ffbeeb67 chore: fmt 2025-04-26 15:49:32 -04:00
Greg Johnston
ee86844077 Merge pull request #3882 from leptos-rs/3871
fix: remove event listeners from Suspense fallback during SSR (closes #3871)
2025-04-26 12:48:45 -04:00
Greg Johnston
1cee3f2f52 Merge branch 'main' into 3871 2025-04-26 12:48:40 -04:00
Greg Johnston
23c89dbfe1 Merge pull request #3878 from leptos-rs/3828
fix: correctly calculate starting index for first new key (closes #3828)
2025-04-26 12:48:12 -04:00
Greg Johnston
9f71f39f89 Merge branch 'main' into 3828 2025-04-26 12:47:56 -04:00
Greg Johnston
ef1d0f108a Merge pull request #3880 from sabify/websocket-example
fix(examples): websocket example tests fail on latency
2025-04-26 12:47:17 -04:00
Greg Johnston
a7a78317b7 Merge pull request #3879 from huuff/prelude-textprop
chore: put `TextProp` in the prelude (closes #3877)
2025-04-26 12:46:53 -04:00
autofix-ci[bot]
5005cc3587 [autofix.ci] apply automated fixes (attempt 2/3) 2025-04-26 16:15:06 +00:00
autofix-ci[bot]
08708f3388 [autofix.ci] apply automated fixes 2025-04-26 16:03:45 +00:00
Greg Johnston
c19c1b32f1 fix: remove event listeners from Suspense fallback during SSR (closes #3871) 2025-04-26 11:48:59 -04:00
Saber Haj Rabiee
e70cc08e96 fix(examples): websocket example tests fail on latency 2025-04-26 07:06:50 -07:00
autofix-ci[bot]
97175663ef [autofix.ci] apply automated fixes 2025-04-26 00:05:33 +00:00
Haf
92524a93cd chore: put TextProp in the prelude (closes #3877) 2025-04-25 23:50:08 +02:00
autofix-ci[bot]
9449f41ca9 [autofix.ci] apply automated fixes (attempt 2/3) 2025-04-25 20:31:56 +00:00
autofix-ci[bot]
d979055b70 [autofix.ci] apply automated fixes 2025-04-25 20:18:03 +00:00
Greg Johnston
97686f71a5 fix: improve support for keyed fields in ArcField<_> 2025-04-25 16:04:01 -04:00
Greg Johnston
06a0c768dc fix: correctly calculate starting index for first new key (closes #3828) 2025-04-25 15:57:30 -04:00
Saber Haj Rabiee
fff6a508fc feat(examples): add WebSocket example (#3853) 2025-04-24 20:17:08 -04:00
Greg Johnston
e65fc23fc7 fix: prevent infinite loop when sending Result over websocket, remove Display bound (#3848)
* chore: easing `Display` bound on `FromServerFnError`, #3811 follow-up

* fix: send/receive websocket data

* fix: clippy warnings

* fix: server_fn_axum example

* fix: make de/serialize_result functions public

* fix: make websocket result ser/de private

* chore: make the doc a comment and remove allow dead_code
2025-04-23 07:52:42 -04:00
Saber Haj Rabiee
f83b14d76c feat: enhancing ByteStream error handling (#3869)
* feat: enhancing `ByteStream` error handling

* fix: easing unnecessary trait bound over some `TextStream` methods
2025-04-23 07:38:39 -04:00
zakstucke
62dac6fb8a fix: prevent ScopedFuture stopping owner cleanup (#3863) 2025-04-23 07:37:12 -04:00
Rakshith Ravi
b36dec8269 feat: add header generation method to BrowserResponse (#3873) 2025-04-23 07:33:18 -04:00
Fea
0c50852251 fix: Use stabilized ClipboardEvent (#3849) 2025-04-23 07:27:24 -04:00
Nikolai Morin
50cb6005a8 chore(examples): complete the migration to Tailwind 4 (#3861)
The tailwind_csr example was not working yet with version 4, and the
tailwind_actix example still had the no-longer-needed config file.

This also brings the three tailwind examples back in sync, mostly. I
don't know why the axum example has a packages.json and
packages-lock.json file, to be honest.
2025-04-23 07:26:56 -04:00
Sathish
b725291ce9 chore: forward lint attributes used with #[component] macro (#3864)
* Forward lint attributes used with #[component] macro

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-04-23 07:26:26 -04:00
Greg Johnston
ed6d45d92d Merge pull request #3870 from leptos-rs/3851
Error boundary fixes
2025-04-23 07:25:41 -04:00
Greg Johnston
73b5587738 fix: correctly insert out-of-order stream chunks when Ok 2025-04-21 13:12:30 -04:00
Greg Johnston
68813a5918 fix: clear old errors when going from error state to error state (closes #3850) 2025-04-21 12:10:47 -04:00
Greg Johnston
8f6a96341e fix: wait for any inner Suspense before rendering ErrorBoundary (closes #3851) 2025-04-21 09:29:33 -04:00
Greg Johnston
046d5286c3 fix: correctly flush synchronous parts of SSR stream when appending another StreamBuilder 2025-04-21 09:28:14 -04:00
Greg Johnston
b45f982feb fix: close Actix websocket stream when browser disconnects (closes #3865) (#3866) 2025-04-19 16:47:09 -04:00
Greg Johnston
2b50ddc0db v0.8.0-rc2 2025-04-18 08:43:16 -04:00
Saber Haj Rabiee
c743f0641c chore: make the doc a comment and remove allow dead_code 2025-04-17 06:45:40 -07:00
Saber Haj Rabiee
078c252e2e fix: make websocket result ser/de private 2025-04-17 03:38:04 -07:00
martin frances
410aedbba8 chore: ran cargo outdated. (#3722)
* chore: ran cargo outdated fixed issues.

In bumping the rand crate, calls to thread_rng() becomes rng().

* backed out changes to rand.
2025-04-16 16:42:01 -07:00
Saber Haj Rabiee
00e474599f fix: reactive_graph keymap impl and clippy warnings (#3843) 2025-04-16 16:39:07 -07:00
Greg Johnston
8f38559aa2 chore(nightly): update proc-macro span file name method name (#3852) 2025-04-16 16:35:59 -07:00
Greg Johnston
3934c8b162 Update issue templates 2025-04-16 09:15:10 -04:00
Saber Haj Rabiee
de3a558203 fix: make de/serialize_result functions public 2025-04-15 09:24:43 -07:00
Saber Haj Rabiee
4d20105760 fix: server_fn_axum example 2025-04-15 09:23:32 -07:00
Saber Haj Rabiee
b95e827b8b fix: clippy warnings 2025-04-15 08:31:42 -07:00
Saber Haj Rabiee
30c445a419 fix: send/receive websocket data 2025-04-15 07:43:47 -07:00
Saber Haj Rabiee
6d5ab73594 chore: easing Display bound on FromServerFnError, #3811 follow-up 2025-04-14 23:52:02 -07:00
Greg Johnston
e0bf5ec480 fix: don't try to move keyed elements within the DOM if they're not yet mounted (closes #3844) (#3846) 2025-04-14 20:06:31 -04:00
Greg Johnston
28d9b3676d fix: correctly decode base64-encoded error messages stored in URL (#3842) 2025-04-13 13:28:58 -04:00
Greg Johnston
bb47916ebe fix: call additional_context after providing other server context in all cases (#3841) 2025-04-12 20:29:43 -04:00
Greg Johnston
cf843a8349 v0.8.0-rc1 2025-04-11 12:03:17 -04:00
Saber Haj Rabiee
7637e586d5 feat: enhancing server_fn errors (#3811)
* feat: enhancing server_fn errors

* fix: `server_fn` `not_result.stderr` line numbers

* fix: example server_fns_axum

* fix: not need to force ser/de traits

* fix(docs): deserialize error comment

* fix: remove unneeded traits bounds
2025-04-10 14:04:16 -07:00
Mykyta
ba7bfd8bac feat: allow using different error types for req/resp with WebSockets, closes #3724 (#3766)
* feat: allow using different error types for request/response with WebSocket

* [autofix.ci] apply automated fixes

* chore: clean up merge issues

* chore: fix custom client example

* we can't use nightly features on stable

* update flake inputs

* include gcc and glib to flake dev shell

* update expected stderr outputs server_fn/tests/invalid

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Greg Johnston <greg.johnston@gmail.com>
2025-04-09 10:03:47 -07:00
Saber Haj Rabiee
8bc4fd4198 Introducing cargo all-features clippy|nextest part of build process (#3767)
* fix: intoducing cargo all-features clippy

* fix: change check to clippy for better diagnostics

* chore: build is redundant

* fix: lint task

* feat: speed up tests by 2x with cargo-nextest

* fix: re-add flags

* fix: router example build process

* fix: correct clippy arguments

* fix: adapt examples makefile to new tools

* fix(CI): add cargo-all-features part of CI dep

* fix: just warn if there is no tests

* fix(CI): add clippy and rustfmt components

* fix: nextest in examples

* fix: clippy should not run on empty set of features in examples
(quickfix)

* fix: pin cargo-all-features installation to a branch

* fix: nextest --no-tests=warn?

* fix: do not use built-in cargo-make workflows

* fix: remove --all-targets

* fix: do not run tests in parallel in certain examples

* fix: disable nextest for examples

* fix: properly override the make task

* chore: run tests with no-deps

* fix: hackernews_islands_axum

* fix(CI): properly use --no-deps

* fix(CI): --no-deps is not supported in tests

* fix(CI): run doctests separately due to stable rust limitation in nextest

* fix(examples): makefile lint recursion

* fix(CI): run tests correctly

* fix: remove unused clear in test makefile

* fix: --no-deps positional arg in clippy

* fix: run doctests for all-features

* fix: running cargo-all-features over doctests

* fix: improve playwright makefile
2025-04-07 08:57:01 -07:00
Alexander Krivács Schrøder
3254d38d2d Revert "Remove getrandom (#3589)" (#3830)
Restores `getrandom` dependency to avoid compilation error when
compiling for WASM with the `nonce` feature.
2025-04-07 08:42:13 -07:00
Greg Johnston
b4abac2614 chore: missing Copy/Clone impls for MappedSignal (#3827) 2025-04-06 19:24:21 -04:00
Greg Johnston
767147f4ab Merge pull request #3802 from sabify/any-spawner-race
fix: prevent race condition in executor initialization + docs, optimization and tests
2025-04-06 15:39:34 -04:00
Greg Johnston
9128545388 fix: do not match static segment with last character missing before slash (closes #3817) (#3823) 2025-04-05 21:58:50 -04:00
Greg Johnston
b8810ba42f fix: correctly establish root ownership for static site generation (closes #3822) (#3824) 2025-04-05 19:50:28 -04:00
Saber Haj Rabiee
6006c33307 Revert "fix: more atomic orderings"
This reverts commit 02f4cfe437.
2025-04-03 09:11:14 -07:00
Saber Haj Rabiee
e113c79fb8 Revert "fix: preserve the ordering of read/write operations on atomic variables"
This reverts commit 2ea0381bb5.
2025-04-03 09:10:20 -07:00
尹吉峰
bdc73594ea change: pass reference to key for Selector::selected (#3694) 2025-04-02 21:43:13 -04:00
Pico Bolero
a55e82afe3 chore: migrate Axum example from Tailwind 3 to Tailwind 4 (#3804)
documentation e.g., input.css and output.css.
Uses @tailwindcss/cli tool to build the output.css
Removes no longer needed tailwind.config.js as that configuration can be
embedded in input.css.
Removes src/style.css as it only included tailwind 3 configurations that
are no longer needed.
2025-04-02 20:58:36 -04:00
Greg Johnston
300d301c11 Merge pull request #3755 from ifiokjr/feat/support_aliased_results_in_server_fn
feat: support aliased `Result` return types for `server_fn`
2025-04-02 20:56:29 -04:00
Greg Johnston
98ee336d0c fix: remove SendOption from public API of actions (#3812) 2025-04-02 13:03:21 -04:00
mahdi739
f102b2f43b fix: Move several Into* trait impls for store fields out of stable module for wider use (#3807)
* Move several Into* trait impls for store fields out of stable module for wider use

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-04-02 09:29:23 -04:00
Greg Johnston
47aebf3e22 fix: allow custom status codes or redirects for route fallbacks (#3808) 2025-04-01 19:25:59 -04:00
jasper
7d3a9da577 chore: add track_caller to store field methods (#3805) 2025-03-30 16:13:44 -04:00
Saber Haj Rabiee
6d90e05c13 fix: do not expose any_spawner::Executor in prelude to encourage local
spawn/spawn_local* implementations
2025-03-30 03:41:48 -07:00
Saber Haj Rabiee
5d0fd5b693 fix: four more spawns usage 2025-03-30 01:52:18 -07:00
Saber Haj Rabiee
d665719b27 fix: no global spawn for wasm executor 2025-03-30 00:28:33 -07:00
Saber Haj Rabiee
ae373b5a52 fix: revert back tick implementation to respect global executor 2025-03-29 23:19:45 -07:00
Saber Haj Rabiee
02f4cfe437 fix: more atomic orderings 2025-03-29 23:19:45 -07:00
Saber Haj Rabiee
40710adfd5 fix: glib test 2025-03-29 23:19:45 -07:00
Saber Haj Rabiee
d0f26ac64b chore: use futures_lite yield_now in glib test 2025-03-29 23:19:45 -07:00
Saber Haj Rabiee
9d2ac0a18f fix: improving tick logic in any_spawner 2025-03-29 23:19:45 -07:00
Saber Haj Rabiee
ab0832ad68 fix: correctly using executator tick instead of timer 2025-03-29 23:19:45 -07:00
Saber Haj Rabiee
2ea0381bb5 fix: preserve the ordering of read/write operations on atomic variables 2025-03-29 23:19:38 -07:00
Saber Haj Rabiee
94acdc24c7 fix: wasm-bindgen unused tests warnings 2025-03-29 23:17:35 -07:00
Saber Haj Rabiee
c175357e69 fix: lib docs 2025-03-29 23:17:35 -07:00
Saber Haj Rabiee
ef86a50c68 fix: wasm in spawn_local condition 2025-03-29 23:17:35 -07:00
Saber Haj Rabiee
e8e0808684 fix: run tests sequentially for glib 2025-03-29 23:17:35 -07:00
Saber Haj Rabiee
4973cc9eff fix: prevent race condition in executor + docs, optimization and tests 2025-03-29 23:17:35 -07:00
Greg Johnston
1a83a78980 Merge pull request #3803 from leptos-rs/store-test-fixes
Store test fixes
2025-03-29 14:55:36 -04:00
ChosunOne
4c6c79da40 feat: add support for more HTTP methods in server fn codecs (#3797) 2025-03-29 14:55:08 -04:00
Greg Johnston
c204df2ff3 Merge pull request #3785 from sabify/fix-portal-example
fix: portal example
2025-03-29 13:56:16 -04:00
zakstucke
0794681011 feat: derive_local for ArcSignal<T, LocalStorage> (#3798) 2025-03-29 13:55:11 -04:00
Greg Johnston
45dc1f4690 chore: update Store Option patch tests to reflect correct behavior 2025-03-29 13:45:39 -04:00
Greg Johnston
b80d58039e fix: prevent sibling over-notification due to #3799) 2025-03-29 13:20:52 -04:00
Greg Johnston
a7619c63bf Merge pull request #3793 from leptos-rs/3783
Improved handling of `<Title/>`
2025-03-28 15:43:45 -04:00
Greg Johnston
4038438cb4 Merge pull request #3799 from leptos-rs/3790
Fix Store notification order for nested keyed fields
2025-03-28 15:43:29 -04:00
Greg Johnston
f23335278b fix: allow repeated class= for all tuples, not only static ones (closes #3794) (#3801) 2025-03-28 15:43:13 -04:00
Greg Johnston
7f51a46b1a fix: correctly notify parent fields when patching 2025-03-27 20:20:04 -04:00
Greg Johnston
a54e3fdb40 fix: notify keyed Store fields from the top down (closes #3790) 2025-03-27 20:19:46 -04:00
Greg Johnston
64ba9b9dd4 fix: do not re-create effect when building a new route 2025-03-27 17:21:16 -04:00
Greg Johnston
568a16b880 chore: clippy 2025-03-26 22:07:32 -04:00
autofix-ci[bot]
d9179f0b19 [autofix.ci] apply automated fixes (attempt 2/3) 2025-03-26 20:05:52 +00:00
autofix-ci[bot]
62161c83c5 [autofix.ci] apply automated fixes 2025-03-26 19:58:41 +00:00
Greg Johnston
97f31f1af5 fix: smarter handling of <Title/> (closes #3783) 2025-03-26 15:46:55 -04:00
Greg Johnston
aa4dd53890 fix: correct insertion of a list of items when the first one doesn't exist in the DOM 2025-03-26 15:46:37 -04:00
Saber Haj Rabiee
a0c6f2b7ac fix: there is no way in cargo-make to override external task, so run
ordinary test for every examples
2025-03-26 08:25:28 -07:00
Ifiok Jr.
753d02a14c chore: fix formatting 2025-03-26 15:12:32 +00:00
Ifiok Jr.
ae23364ef8 chore: add comment explaining annotation 2025-03-26 15:12:32 +00:00
Ifiok Jr.
a74713a371 fix: only use diagnostic attribute on versions after 1.78 2025-03-26 15:12:31 +00:00
Ifiok Jr.
72e84f4e38 feat: support aliased Result return types for server_fn
- Enhanced error messages for better clarity when server functions do not meet the expected return type requirements.
- Added `trybuild` tests to ensure behaviour is not broken in future releases.
2025-03-26 15:12:31 +00:00
Ifiok Jr.
0a13ebd94d fix: also include ServerFnErrorErr in prelude 2025-03-26 15:12:30 +00:00
Ifiok Jr.
23c6f548ec fix: add FromServerFnError to leptos::prelude::* 2025-03-26 15:12:30 +00:00
Saber Haj Rabiee
543063ac24 fix: run cargo test for portal eample (minify tests) 2025-03-26 08:07:43 -07:00
Saber Haj Rabiee
04da08f82d fix: portal example makefile to run cargo test 2025-03-25 23:45:14 -07:00
Saber Haj Rabiee
c8ee4b3e05 fix: portal minify test 2025-03-25 23:42:09 -07:00
Saber Haj Rabiee
999a09d064 fix: quoting strings in portal example 2025-03-25 23:27:29 -07:00
Saber Haj Rabiee
9a32ae7bd5 fix: write simple minifier for portal tests 2025-03-25 23:12:19 -07:00
zakstucke
a96ef29d9e feat: re-export the codee crate to make custom encodings for resources easier (#3761) 2025-03-25 21:40:48 -04:00
Saber Haj Rabiee
0553c2af30 fix: signal CI to install chromedriver 2025-03-25 12:58:14 -07:00
Saber Haj Rabiee
668b7b4adb fix: limit opt-level to test 2025-03-25 12:57:51 -07:00
Saber Haj Rabiee
0a90def2da fix: portal example too many locals 2025-03-25 12:32:44 -07:00
Saber Haj Rabiee
d095879ef2 fix: portal example 2025-03-25 12:16:19 -07:00
Saber Haj Rabiee
2508b016d5 fix(CI): run only the exact examples on the only examples change (#3782)
* fix(CI): run only the exact examples on the only examples change

* chore: format html (retrigger ci)

* fix: missing the right field

* fix(CI): add missing output key

* fix(CI): properly set matrix directory for test-only-examples

* fix(CI): missing bracket token

* fix(CI): do not run semver check on examples only tests

* fix(CI): semver logic
2025-03-25 09:38:25 -07:00
Saber Haj Rabiee
bf35298708 fix: router example build process (#3779)
* fix: router example build process

* fix: a working router example
2025-03-24 23:53:45 -07:00
Greg Johnston
1a1e436cff fix: correctly handle optional parameters in ParentRoute (#3784) 2025-03-24 20:15:31 -04:00
Greg Johnston
b4731e61c6 Merge pull request #3781 from leptos-rs/partialeq-sendoption
Tweaks to `MaybeSendWrapperOption<_>`
2025-03-24 20:12:06 -04:00
autofix-ci[bot]
5bdc68a2f2 [autofix.ci] apply automated fixes 2025-03-24 21:56:27 +00:00
Greg Johnston
0a6cd9e33f update example 2025-03-24 17:40:52 -04:00
Greg Johnston
eca8e3cc72 chore: update tests 2025-03-23 20:33:51 -04:00
Greg Johnston
e0856eee33 chore: remove duplicate implementation due to merge 2025-03-23 20:20:30 -04:00
Greg Johnston
514cf62bf6 docs: add some more documentation to SendOption 2025-03-23 20:20:12 -04:00
Greg Johnston
26522666d5 chore: rename MaybeSendWrapperOption to SendOption 2025-03-23 20:19:30 -04:00
Greg Johnston
dcb3f202cd fix: correctly handle == and similar traits for MaybeSendWrapperOption 2025-03-23 20:14:43 -04:00
Greg Johnston
b032c15a84 Merge pull request #3762 from leptos-rs/reduce-local-signals
Reduce use local signals for `Action::new_local` and similar primitives
2025-03-23 15:55:02 -04:00
Saber Haj Rabiee
f24e804985 fix(CI): toolchain will be determined and test against by CI (#3778)
* fix(CI): toolchain will be determined and test against by CI

* chore: formatting examples

* fix: clippy
2025-03-23 10:10:52 -07:00
Alexander Krivács Schrøder
2c1194a71a docs: correct docs for default value for hash_files (closes #3774) (#3775)
Resolves #3774
2025-03-23 09:08:20 -04:00
Greg Johnston
fcdc9a07e4 Merge pull request #3777 from sabify/fix-ci-examples-logic
fix(CI): conditional executions of only changed examples
2025-03-23 09:07:16 -04:00
Greg Johnston
3d5b0127c2 Merge pull request #3776 from sabify/remove-ci-cache
fix(CI): remove caching
2025-03-23 09:06:59 -04:00
Saber Haj Rabiee
1cbbcda48b fix(CI): conditional executions of only changed examples 2025-03-23 01:42:53 -07:00
Saber Haj Rabiee
d9b998ed0c fix(CI): add was32 target in rust-toolchain setup 2025-03-23 01:12:46 -07:00
Saber Haj Rabiee
79f0482325 fix(CI): install trunk with bininstall 2025-03-23 01:12:13 -07:00
Saber Haj Rabiee
78d70fc400 fix(CI): caching may not be needed 2025-03-23 01:11:27 -07:00
Saber Haj Rabiee
2a011dd89c fix(CI): install deno only if needed, #3768 follow-up (#3773) 2025-03-23 00:04:07 -07:00
Saber Haj Rabiee
bc7c59d880 fix: clippy errors (#3772)
* fix: use map instead of if else

* fix: needless lifetime
2025-03-22 23:44:49 -07:00
Greg Johnston
a5d158765f fix: support IntoFragment for single element (closes #3757) (#3759) 2025-03-22 19:55:16 -04:00
Saber Haj Rabiee
999c8be6a0 fix(CI): install deps only if needed, speeds up CI (#3768) 2025-03-22 15:30:53 -07:00
Greg Johnston
5cfb16c79f feat: consolidate sync/unsync Action APIs 2025-03-22 15:24:10 -04:00
Greg Johnston
fd95f11dff Revert "fix: use signals rather than Action::new_local() (closes #3746) (#3749)"
This reverts commit 2658ae8b40.
2025-03-22 15:24:10 -04:00
Greg Johnston
2d8336ac91 feat: consolidate sync/unsync Action APIs 2025-03-22 15:24:10 -04:00
Saber Haj Rabiee
bb0c352f75 fix: remove duplicate semver (#3764) 2025-03-22 11:50:43 -07:00
Saber Haj Rabiee
b6eddca2c1 fix(CI): optimize CI workflow (#3758)
* chore: optimize CI

* chore: fomratting makefile to retrigger ci

* fix: run tests against both stable and nightly toolchains

* chore: simplify workflow

* fix(CI): remove unused inputs

* fix(CI): correctly indicating erased mode

* fix(CI): replace input with the name

* fix(CI): fixed logic of running cargo cmake on workspace on each example
and memeber!

* fix(CI): organized, simplified and unified CI jobs

* chore: change CI name

* fix(CI): keyword

* fix(CI): wait for dependent jobs
2025-03-22 09:48:54 -07:00
Saber Haj Rabiee
57272926ad fix: properly feature gating ui macro tests (Closes #3742) (#3756)
* fix: properly feature gating ui macro tests (Closes #3742)

* fix: wrongly gated main in test and added more gates

* docs: formatting
2025-03-22 09:48:08 -07:00
Greg Johnston
e903c297a9 chore: add SignalSetter to prelude (closes #3547) (#3753) 2025-03-21 16:58:00 -04:00
Nicolas Cura
b6e6588c8d fix: make trailing comma optional for either!() macro (#3736) 2025-03-21 16:39:52 -04:00
Greg Johnston
713abb3072 chore: ensure WASM target is installed for examples with provided rust-toolchain.toml (closes #3717) (#3752) 2025-03-21 16:38:35 -04:00
Greg Johnston
fce202db39 Merge pull request #3529 from leptos-rs/leptos_0.8
Leptos 0.8
2025-03-21 16:18:55 -04:00
Greg Johnston
1fe39bf7c8 Merge remote-tracking branch 'origin' into leptos_0.8 2025-03-21 16:18:01 -04:00
Jérôme MEVEL
fb5fdfc429 docs: in Effect::watch, refer to 'dependency_fn' and 'handler' in doc (#3731) 2025-03-21 16:01:56 -04:00
Alexander Krivács Schrøder
c738c5d81b feat: switch extract() helper to use ServerFnErrorErr (closes #3745) (#3750)
* feat: switch `extract()` helper to use `ServerFnErrorErr`

This provides much better compatibility with server functions using
custom errors while still working as before with server functions using
`ServerFnError`

* style: apply autofix diff
2025-03-21 16:01:12 -04:00
Greg Johnston
2658ae8b40 fix: use signals rather than Action::new_local() (closes #3746) (#3749) 2025-03-21 15:51:44 -04:00
Saber Haj Rabiee
a0d75fda03 chore: no need to filter out "nightly" feature as of #3735 (#3747)
* chore: no need to filter out "nightly" feature as of #3735

* fix: enable hot-reload only if in all debug mode, nightly feature and
nightly rustc

* fix: do not build/test against nightly feature alone

* fix: don not test against nightly feature alone

* fix: add trace-components to denylist
2025-03-21 10:16:16 -07:00
Saber Haj Rabiee
716c770a45 fix(CI): sermver job name (#3748) 2025-03-21 06:42:42 -07:00
Saber Haj Rabiee
a2d268606c fix(CI): cleanup directory no matter of results (#3743) 2025-03-20 17:53:25 -07:00
zakstucke
90fc727d60 ArcLocalResource fix (#3740) 2025-03-20 16:20:05 -07:00
zakstucke
aae827923c ArcLocalResource fix (0.8) (#3741) 2025-03-20 16:19:32 -07:00
Saber Haj Rabiee
52c770c7da fix(CI): free-up disk, properly gate nightly feature and pre-install deps (#3735)
* fix(CI): pre-install tailwind and sass

* chore: retrigger CI

* fix(CI): properly set `erased_mode` in examples

* fix(CI): workflow logic install deps locally

* chore(CI): organized tailwind and sass versions

* fix: properly ignore nightly features on stable rustc

* fix(CI): typo

* fix: missing cfg attr

* fix: properly gate nightly module in `reactive_graph``

* fix: complete `rustc_nightly` cfg gate

* fix(CI): making even more space for CI workers

* fix: missing one more rustc_nightly gated feature

* fix: maximize the speed of cargo-all-features by limiting combinations to 2
2025-03-20 14:28:07 -07:00
Saber Haj Rabiee
9210636266 fix(CI): cancel in-group inflight and pending jobs on new pushes in CI (#3739) 2025-03-20 13:02:17 -07:00
Saber Haj Rabiee
012616c4d8 fix(CI): switch to stable in semver for most compatibility (#3737) 2025-03-20 13:01:45 -07:00
Evan Almloff
6b9520f1a9 More flexible server fn macro api (#3725)
* Split up the server function expansion into multiple functions

* Use LitStr instead of Literal

* Refactor out more of the expansion

* Fix the parse example

* Expose some of the parsed server function arguments and body fields

* mark ServerFnBody as non_exhaustive

* [autofix.ci] apply automated fixes

* Fix server function paths

* rename from impl

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-03-20 09:55:38 -07:00
benwis
9052804ab4 v0.7.8 2025-03-20 08:21:11 -07:00
benwis
e95c903e85 v0.7.7 2025-03-19 18:19:37 -07:00
bimoadityar
8a179e6f45 examples: update example tailwind input css to v4 (#3702) 2025-03-19 21:01:03 -04:00
Greg Johnston
e765f99016 fix: matching optional params after an initial static param (closes #3730) (#3732) 2025-03-19 20:59:41 -04:00
Nicolas Cura
30548eca31 Merge pull request #3727 from NCura/patch-1
Fix typo
2025-03-18 14:30:23 -04:00
365 changed files with 9667 additions and 3876 deletions

View File

@@ -29,11 +29,14 @@ Steps to reproduce the behavior:
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Next Steps**
[ ] I will make a PR
[ ] I would like to make a PR, but need help getting started
[ ] I want someone else to take the time to fix this
[ ] This is a low priority for me and is just shared for your information
**Additional context**
Add any other context about the problem here.

View File

@@ -21,33 +21,19 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
with: {toolchain: nightly, components: "rustfmt, clippy", target: "wasm32-unknown-unknown", rustflags: ""}
with: {toolchain: "nightly-2025-04-16", 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 cargo-all-features
run: cargo install --git https://github.com/sabify/cargo-all-features --branch arbitrary-command-support
- name: Install jq
run: sudo apt-get install jq
- run: |
echo "Formatting the workspace"
cargo fmt --all
echo "Running Clippy against each member's features (default features included)"
for member in $(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | .name'); do
echo "Working on member $member":
echo -e "\tdefault-features/no-features:"
# this will also run on members with no features or default features
cargo clippy --allow-dirty --fix --lib --package "$member"
features=$(cargo metadata --no-deps --format-version 1 | jq -r ".packages[] | select(.name == \"$member\") | .features | keys[]")
for feature in $features; do
if [ "$feature" = "default" ]; then
continue
fi
echo -e "\tfeature $feature"
cargo clippy --allow-dirty --fix --lib --package "$member" --features "$feature"
done
done
- name: Format the workspace
run: cargo fmt --all
- name: Clippy the workspace
run: cargo all-features clippy --allow-dirty --fix --lib --no-deps
- uses: autofix-ci/action@v1.3.1
if: ${{ always() }}
with:

View File

@@ -1,33 +0,0 @@
name: CI Changed Examples
on:
push:
branches:
- main
- leptos_0.6
- leptos_0.8
pull_request:
branches:
- main
- leptos_0.6
- leptos_0.8
jobs:
get-example-changed:
uses: ./.github/workflows/get-example-changed.yml
get-matrix:
needs: [get-example-changed]
uses: ./.github/workflows/get-changed-examples-matrix.yml
with:
example_changed: ${{ fromJSON(needs.get-example-changed.outputs.example_changed) }}
test:
name: CI
needs: [get-example-changed, get-matrix]
if: needs.get-example-changed.outputs.example_changed == 'true'
strategy:
matrix: ${{ fromJSON(needs.get-matrix.outputs.matrix) }}
fail-fast: false
uses: ./.github/workflows/run-cargo-make-task.yml
with:
directory: ${{ matrix.directory }}
erased_mode: ${{ matrix.erased_mode }}
cargo_make_task: "ci"
toolchain: stable

View File

@@ -1,30 +0,0 @@
name: CI Examples
on:
push:
branches:
- main
- leptos_0.6
- leptos_0.8
pull_request:
branches:
- main
- leptos_0.6
- leptos_0.8
jobs:
get-leptos-changed:
uses: ./.github/workflows/get-leptos-changed.yml
get-examples-matrix:
uses: ./.github/workflows/get-examples-matrix.yml
test:
name: CI
needs: [get-leptos-changed, get-examples-matrix]
if: needs.get-leptos-changed.outputs.leptos_changed == 'true'
strategy:
matrix: ${{ fromJSON(needs.get-examples-matrix.outputs.matrix) }}
fail-fast: false
uses: ./.github/workflows/run-cargo-make-task.yml
with:
directory: ${{ matrix.directory }}
erased_mode: ${{ matrix.erased_mode }}
cargo_make_task: "ci"
toolchain: stable

View File

@@ -1,33 +0,0 @@
name: CI semver
on:
push:
branches:
- main
- leptos_0.6
- leptos_0.8
pull_request:
branches:
- main
- leptos_0.6
- leptos_0.8
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'
name: Run semver check (nightly-2025-03-05)
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
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
rust-toolchain: nightly-2025-03-05

View File

@@ -10,13 +10,20 @@ on:
- main
- leptos_0.6
- leptos_0.8
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
get-leptos-changed:
uses: ./.github/workflows/get-leptos-changed.yml
get-leptos-matrix:
uses: ./.github/workflows/get-leptos-matrix.yml
test:
name: CI
get-example-changed:
uses: ./.github/workflows/get-example-changed.yml
get-examples-matrix:
uses: ./.github/workflows/get-examples-matrix.yml
test-members:
name: CI (members)
needs: [get-leptos-changed, get-leptos-matrix]
if: needs.get-leptos-changed.outputs.leptos_changed == 'true'
strategy:
@@ -25,6 +32,37 @@ jobs:
uses: ./.github/workflows/run-cargo-make-task.yml
with:
directory: ${{ matrix.directory }}
erased_mode: ${{ matrix.erased_mode }}
cargo_make_task: "ci"
toolchain: nightly-2025-03-05
test-examples:
name: CI (examples)
needs: [test-members, get-examples-matrix]
if: ${{ success() }}
strategy:
matrix: ${{ fromJSON(needs.get-examples-matrix.outputs.matrix) }}
fail-fast: false
uses: ./.github/workflows/run-cargo-make-task.yml
with:
directory: ${{ matrix.directory }}
test-only-examples:
name: CI (examples)
needs: [get-leptos-changed, get-example-changed]
if: needs.get-leptos-changed.outputs.leptos_changed != 'true' && needs.get-example-changed.outputs.example_changed == 'true'
strategy:
matrix: ${{ fromJSON(needs.get-example-changed.outputs.matrix) }}
fail-fast: false
uses: ./.github/workflows/run-cargo-make-task.yml
with:
directory: ${{ matrix.directory }}
semver-check:
name: SemVer check (stable)
needs: [get-leptos-changed, test-members, test-examples]
if: ${{ success() && needs.get-leptos-changed.outputs.leptos_changed == 'true' && !contains(github.event.pull_request.labels.*.name, 'breaking') }}
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
uses: obi1kenobi/cargo-semver-checks-action@v2

View File

@@ -1,54 +0,0 @@
name: Changed Examples Matrix Call
on:
workflow_call:
inputs:
example_changed:
description: "Example Changed"
required: true
type: boolean
outputs:
matrix:
description: "Matrix"
value: ${{ jobs.get-example-changed.outputs.matrix }}
jobs:
get-example-changed:
name: Get Changed Example Matrix
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get example project directories that changed
id: changed-dirs
uses: tj-actions/changed-files@v45
with:
dir_names: true
dir_names_max_depth: "2"
files: |
examples/**
!examples/cargo-make/**
!examples/gtk/**
!examples/Makefile.toml
!examples/*.md
json: true
quotepath: false
- name: List example project directories that changed
run: echo '${{ steps.changed-dirs.outputs.all_changed_files }}'
- name: Set Matrix
id: set-matrix
run: |
if [ ${{ inputs.example_changed }} == 'true' ]; then
# Create matrix with changed directories
echo "matrix={\"directory\":${{ steps.changed-dirs.outputs.all_changed_files }}}" >> "$GITHUB_OUTPUT"
else
# Create matrix with one item to prevent an empty vector error
echo "matrix={\"directory\":[\"NO_CHANGE\"], \"erased_mode\": [false, true]}" >> "$GITHUB_OUTPUT"
fi

View File

@@ -5,12 +5,18 @@ on:
example_changed:
description: "Example Changed"
value: ${{ jobs.get-example-changed.outputs.example_changed }}
# This is for test-only-examples workflow in ci.yml
matrix:
description: "Example Changed Directories"
value: ${{ jobs.get-example-changed.outputs.matrix }}
jobs:
get-example-changed:
name: Get Example Changed
runs-on: ubuntu-latest
outputs:
example_changed: ${{ steps.set-example-changed.outputs.example_changed }}
# This is for test-only-examples workflow in ci.yml
matrix: ${{ steps.set-example-changed.outputs.matrix }}
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -18,7 +24,7 @@ jobs:
fetch-depth: 0
- name: Get example files that changed
id: changed-files
uses: tj-actions/changed-files@v45
uses: tj-actions/changed-files@v46
with:
files: |
examples/**
@@ -27,7 +33,11 @@ jobs:
!examples/*.md
- name: List example files that changed
run: echo '${{ steps.changed-files.outputs.all_changed_files }}'
- name: Install jq
run: sudo apt-get install jq
- name: Set example_changed
id: set-example-changed
run: |
echo "example_changed=${{ steps.changed-files.outputs.any_changed }}" >> "$GITHUB_OUTPUT"
# This is for test-only-examples workflow in ci.yml
echo "matrix={\"directory\": $(echo '${{ steps.changed-files.outputs.all_changed_files }}' | tr ' ' '\n' | awk -F'/' '{print $1 "/" $2}'| sort -u | jq -R -s -c 'split("\n") | .[:-1]')}" >> "$GITHUB_OUTPUT"

View File

@@ -28,7 +28,7 @@ jobs:
sed 's/\/$//' |
jq -R -s -c 'split("\n")[:-1]')
echo "Example Directories: $examples"
echo "matrix={\"directory\":$examples, \"erased_mode\": [false, true]}" >> "$GITHUB_OUTPUT"
echo "matrix={\"directory\":$examples}" >> "$GITHUB_OUTPUT"
- name: Print Location Info
run: |
echo "Workspace: ${{ github.workspace }}"

View File

@@ -18,7 +18,7 @@ jobs:
fetch-depth: 0
- name: Get source files that changed
id: changed-source
uses: tj-actions/changed-files@v45
uses: tj-actions/changed-files@v46
with:
files_ignore: |
.*/**/*

View File

@@ -24,7 +24,7 @@ jobs:
sed "s|$(pwd)/||" |
jq -R -s -c 'split("\n")[:-1]')
echo "Leptos Directories: $crates"
echo "matrix={\"directory\":$crates, \"erased_mode\": [false, true]}" >> "$GITHUB_OUTPUT"
echo "matrix={\"directory\":$crates}" >> "$GITHUB_OUTPUT"
- name: Print Location Info
run: |
echo "Workspace: ${{ github.workspace }}"

View File

@@ -5,24 +5,21 @@ on:
directory:
required: true
type: string
erased_mode:
required: true
type: boolean
cargo_make_task:
required: true
type: string
toolchain:
required: true
type: string
env:
CARGO_TERM_COLOR: always
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
DEBIAN_FRONTEND: noninteractive
RUSTFLAGS: ${{ inputs.erased_mode && '--cfg erase_components' || '' }}
LEPTOS_TAILWIND_VERSION: v4.0.14
LEPTOS_SASS_VERSION: 1.86.0
jobs:
test:
name: "Run ${{ inputs.cargo_make_task }} (${{ inputs.toolchain }}) (erased_mode: ${{ inputs.erased_mode }})"
name: "Run (${{ matrix.toolchain }}) (erased_mode: ${{ matrix.erased_mode && 'enabled' || 'disabled' }})"
runs-on: ubuntu-latest
strategy:
matrix:
toolchain: [stable, nightly-2025-04-16]
erased_mode: [true, false]
steps:
- name: Free Disk Space
run: |
@@ -30,11 +27,25 @@ jobs:
df -h
sudo rm -rf /usr/local/.ghcup
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo rm -rf /usr/local/lib/android/sdk/ndk
sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/share/boost
sudo apt-get clean
sudo rm -rf /usr/local/lib/node_modules
# following lines currenly not needed as it takes too much time
# the new isolated CI doesn't need much space to test libraries
#
# uncommet only if nneded
#
# sudo apt-get clean
# sudo apt-get purge -y '^ghc-.*' '^dotnet-.*' '^llvm-.*' '^mono-.*' '^php.*' '^ruby.*'
# sudo apt-get autoremove -y
# sudo apt-get clean
# sudo rm -rf "$AGENT_TOOLSDIRECTORY"
# docker system prune -af
# docker image prune -af
# docker volume prune -f
echo "Disk space after cleanup:"
df -h
# Setup environment
@@ -46,41 +57,46 @@ jobs:
- name: Setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ inputs.toolchain }}
- name: Add wasm32-unknown-unknown
run: rustup target add wasm32-unknown-unknown
- name: Setup cargo-make
uses: davidB/rust-cargo-make@v1
- name: Cargo generate-lockfile
run: cargo generate-lockfile
- uses: Swatinem/rust-cache@v2
toolchain: ${{ matrix.toolchain }}
targets: wasm32-unknown-unknown
components: clippy,rustfmt
- name: Install binstall
uses: cargo-bins/cargo-binstall@main
- name: Install wasm-bindgen
run: cargo binstall wasm-bindgen-cli --no-confirm
- name: Install cargo-leptos
run: cargo binstall cargo-leptos --locked --no-confirm
- name: Install cargo-make
run: cargo binstall cargo-make --no-confirm
- name: Install nextest
run: cargo binstall cargo-nextest --no-confirm
- name: Install cargo-all-features
run: cargo install --git https://github.com/sabify/cargo-all-features --branch arbitrary-command-support
- name: Install Trunk
uses: jetli/trunk-action@v0.5.0
with:
version: "latest"
if: contains(inputs.directory, 'examples')
run: cargo binstall trunk --no-confirm
- name: Print Trunk Version
if: contains(inputs.directory, 'examples')
run: trunk --version
- name: Install Node.js
if: contains(inputs.directory, 'examples')
uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v4
name: Install pnpm
if: contains(inputs.directory, 'examples')
id: pnpm-install
with:
version: 8
run_install: false
- name: Get pnpm store directory
if: contains(inputs.directory, 'examples')
id: pnpm-cache
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
if: contains(inputs.directory, 'examples')
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
@@ -88,8 +104,9 @@ jobs:
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Maybe install chromedriver
if: contains(inputs.directory, 'examples')
run: |
project_makefile=${{inputs.directory}}/Makefile.toml
project_makefile='${{inputs.directory}}/Makefile.toml'
webdriver_count=$(cat $project_makefile | grep "cargo-make/webdriver.toml" | wc -l)
if [ $webdriver_count -eq 1 ]; then
if ! command -v chromedriver &>/dev/null; then
@@ -103,8 +120,9 @@ jobs:
echo chromedriver is not required
fi
- name: Maybe install playwright browser dependencies
if: contains(inputs.directory, 'examples')
run: |
for pw_path in $(find ${{inputs.directory}} -name playwright.config.ts)
for pw_path in $(find '${{inputs.directory}}' -name playwright.config.ts)
do
pw_dir=$(dirname $pw_path)
if [ ! -v $pw_dir ]; then
@@ -116,17 +134,41 @@ jobs:
fi
done
- name: Install Deno
if: contains(inputs.directory, 'examples')
uses: denoland/setup-deno@v2
with:
deno-version: v1.x
- name: Maybe install gtk-rs dependencies
if: contains(inputs.directory, 'gtk')
run: |
if [ ! -z $(echo ${{inputs.directory}} | grep gtk) ]; then
sudo apt-get update
sudo apt-get install -y libglib2.0-dev libgio2.0-cil-dev libgraphene-1.0-dev libcairo2-dev libpango1.0-dev libgtk-4-dev
fi
sudo apt-get install -y libglib2.0-dev libgio2.0-cil-dev libgraphene-1.0-dev libcairo2-dev libpango1.0-dev libgtk-4-dev
- name: Install Tailwind and Sass dependencies
if: contains(inputs.directory, 'examples')
run: |
cd '${{ inputs.directory }}'
tailwindcss_version=$(echo "$LEPTOS_TAILWIND_VERSION" | sed 's/^v//')
sass_version="$LEPTOS_SASS_VERSION"
pnpm add "tailwindcss@$tailwindcss_version" "@tailwindcss/cli@$tailwindcss_version" "sass@$sass_version"
echo "Tailwind CSS version:"
./node_modules/.bin/tailwindcss --version
echo "Sass version:"
./node_modules/.bin/sass --version
# Run Cargo Make Task
- name: ${{ inputs.cargo_make_task }}
run: |
cd ${{ inputs.directory }}
cargo make --profile=github-actions ${{ inputs.cargo_make_task }}
cd '${{ inputs.directory }}'
cargo make --no-workspace --profile=github-actions ci
# Check if the counter_isomorphic can be built with leptos_debuginfo cfg flag in release mode
- name: ${{ inputs.cargo_make_task }} with --cfg=leptos_debuginfo
if: contains(inputs.directory, 'counter_isomorphic')
run: |
cd '${{ inputs.directory }}'
RUSTFLAGS="$RUSTFLAGS --cfg leptos_debuginfo" cargo leptos build --release
- name: Clean up ${{ inputs.directory }}
if: always()
run: |
cd '${{ inputs.directory }}'
cargo clean || true
rm -rf node_modules || true

372
Cargo.lock generated
View File

@@ -263,15 +263,18 @@ dependencies = [
[[package]]
name = "any_spawner"
version = "0.2.1"
version = "0.3.0"
dependencies = [
"async-executor",
"futures",
"futures-lite",
"glib",
"serial_test",
"thiserror 2.0.12",
"tokio",
"tracing",
"wasm-bindgen-futures",
"wasm-bindgen-test",
]
[[package]]
@@ -334,9 +337,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
version = "0.1.87"
version = "0.1.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97"
checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
dependencies = [
"proc-macro2",
"quote",
@@ -390,9 +393,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "axum"
version = "0.8.1"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8"
checksum = "de45108900e1f9b9242f7f2e254aa3e2c029c921c258fe9e6b4217eeebd54288"
dependencies = [
"axum-core",
"base64",
@@ -428,12 +431,12 @@ dependencies = [
[[package]]
name = "axum-core"
version = "0.5.0"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733"
checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
dependencies = [
"bytes",
"futures-util",
"futures-core",
"http 1.3.1",
"http-body",
"http-body-util",
@@ -561,9 +564,9 @@ checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
[[package]]
name = "cc"
version = "1.2.16"
version = "1.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
dependencies = [
"jobserver",
"libc",
@@ -684,9 +687,9 @@ dependencies = [
[[package]]
name = "const-str"
version = "0.5.7"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3618cccc083bb987a415d85c02ca6c9994ea5b44731ec28b9ecf09658655fba9"
checksum = "9e991226a70654b49d34de5ed064885f0bef0348a8e70018b8ff1ac80aa984a2"
[[package]]
name = "const_format"
@@ -729,9 +732,9 @@ dependencies = [
[[package]]
name = "convert_case"
version = "0.7.1"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f"
dependencies = [
"unicode-segmentation",
]
@@ -831,9 +834,9 @@ checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
[[package]]
name = "deranged"
version = "0.3.11"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058"
dependencies = [
"powerfmt",
]
@@ -918,7 +921,7 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "either_of"
version = "0.1.5"
version = "0.1.6"
dependencies = [
"paste",
"pin-project-lite",
@@ -986,9 +989,9 @@ dependencies = [
[[package]]
name = "event-listener-strategy"
version = "0.5.3"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
"event-listener",
"pin-project-lite",
@@ -1018,9 +1021,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foreign-types"
@@ -1174,14 +1177,16 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets 0.52.6",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
"wasm-bindgen",
]
[[package]]
@@ -1325,9 +1330,9 @@ dependencies = [
[[package]]
name = "half"
version = "2.4.1"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1"
dependencies = [
"cfg-if",
"crunchy",
@@ -1586,9 +1591,9 @@ dependencies = [
[[package]]
name = "icu_locid_transform_data"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
[[package]]
name = "icu_normalizer"
@@ -1610,9 +1615,9 @@ dependencies = [
[[package]]
name = "icu_normalizer_data"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
[[package]]
name = "icu_properties"
@@ -1631,9 +1636,9 @@ dependencies = [
[[package]]
name = "icu_properties_data"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
[[package]]
name = "icu_provider"
@@ -1776,13 +1781,14 @@ checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]]
name = "leptos"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"any_spawner",
"base64",
"cfg-if",
"either_of",
"futures",
"getrandom 0.2.15",
"hydration_context",
"leptos-spin-macro",
"leptos_config",
@@ -1796,6 +1802,7 @@ dependencies = [
"rand 0.8.5",
"reactive_graph",
"rustc-hash 2.1.1",
"rustc_version",
"send_wrapper",
"serde",
"serde_json",
@@ -1806,8 +1813,8 @@ dependencies = [
"thiserror 2.0.12",
"throw_error",
"tracing",
"typed-builder",
"typed-builder-macro",
"typed-builder 0.21.0",
"typed-builder-macro 0.21.0",
"wasm-bindgen",
"web-sys",
]
@@ -1826,7 +1833,7 @@ dependencies = [
[[package]]
name = "leptos_actix"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"actix-files",
"actix-http",
@@ -1852,7 +1859,7 @@ dependencies = [
[[package]]
name = "leptos_axum"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"any_spawner",
"axum",
@@ -1876,7 +1883,7 @@ dependencies = [
[[package]]
name = "leptos_config"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"config",
"regex",
@@ -1885,12 +1892,12 @@ dependencies = [
"tempfile",
"thiserror 2.0.12",
"tokio",
"typed-builder",
"typed-builder 0.21.0",
]
[[package]]
name = "leptos_dom"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"js-sys",
"leptos",
@@ -1907,7 +1914,7 @@ dependencies = [
[[package]]
name = "leptos_hot_reload"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"anyhow",
"camino",
@@ -1923,7 +1930,7 @@ dependencies = [
[[package]]
name = "leptos_integration_utils"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"futures",
"hydration_context",
@@ -1936,11 +1943,11 @@ dependencies = [
[[package]]
name = "leptos_macro"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"attribute-derive",
"cfg-if",
"convert_case 0.7.1",
"convert_case 0.8.0",
"html-escape",
"insta",
"itertools",
@@ -1953,19 +1960,20 @@ dependencies = [
"proc-macro2",
"quote",
"rstml",
"rustc_version",
"serde",
"server_fn",
"server_fn_macro 0.8.0-beta",
"server_fn_macro 0.8.2",
"syn 2.0.100",
"tracing",
"trybuild",
"typed-builder",
"typed-builder 0.20.1",
"uuid",
]
[[package]]
name = "leptos_meta"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"futures",
"indexmap",
@@ -1980,7 +1988,7 @@ dependencies = [
[[package]]
name = "leptos_router"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"any_spawner",
"either_of",
@@ -1993,6 +2001,7 @@ dependencies = [
"or_poisoned",
"percent-encoding",
"reactive_graph",
"rustc_version",
"send_wrapper",
"tachys",
"thiserror 2.0.12",
@@ -2004,7 +2013,7 @@ dependencies = [
[[package]]
name = "leptos_router_macro"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"leptos_macro",
"leptos_router",
@@ -2016,7 +2025,7 @@ dependencies = [
[[package]]
name = "leptos_server"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"any_spawner",
"base64",
@@ -2055,9 +2064,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.9.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9"
checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
[[package]]
name = "litemap"
@@ -2094,9 +2103,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.26"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "manyhow"
@@ -2160,6 +2169,16 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "minicov"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b"
dependencies = [
"cc",
"walkdir",
]
[[package]]
name = "miniserde"
version = "0.1.42"
@@ -2295,9 +2314,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.21.0"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "openssl"
@@ -2462,9 +2481,9 @@ dependencies = [
[[package]]
name = "prettyplease"
version = "0.2.30"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a"
checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb"
dependencies = [
"proc-macro2",
"syn 2.0.100",
@@ -2580,11 +2599,12 @@ dependencies = [
[[package]]
name = "quinn"
version = "0.11.6"
version = "0.11.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef"
checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012"
dependencies = [
"bytes",
"cfg_aliases",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
@@ -2594,17 +2614,18 @@ dependencies = [
"thiserror 2.0.12",
"tokio",
"tracing",
"web-time",
]
[[package]]
name = "quinn-proto"
version = "0.11.9"
version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d"
checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
dependencies = [
"bytes",
"getrandom 0.2.15",
"rand 0.8.5",
"getrandom 0.3.2",
"rand 0.9.0",
"ring",
"rustc-hash 2.1.1",
"rustls",
@@ -2618,9 +2639,9 @@ dependencies = [
[[package]]
name = "quinn-udp"
version = "0.5.10"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944"
checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5"
dependencies = [
"cfg_aliases",
"libc",
@@ -2661,6 +2682,12 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rancor"
version = "0.1.0"
@@ -2727,12 +2754,12 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom 0.3.1",
"getrandom 0.3.2",
]
[[package]]
name = "reactive_graph"
version = "0.2.0-beta"
version = "0.2.2"
dependencies = [
"any_spawner",
"async-lock",
@@ -2742,6 +2769,7 @@ dependencies = [
"or_poisoned",
"pin-project-lite",
"rustc-hash 2.1.1",
"rustc_version",
"send_wrapper",
"serde",
"slotmap",
@@ -2754,9 +2782,10 @@ dependencies = [
[[package]]
name = "reactive_stores"
version = "0.2.0-beta"
version = "0.2.2"
dependencies = [
"any_spawner",
"dashmap",
"guardian",
"itertools",
"leptos",
@@ -2765,15 +2794,16 @@ dependencies = [
"reactive_graph",
"reactive_stores_macro",
"rustc-hash 2.1.1",
"send_wrapper",
"tokio",
"tokio-test",
]
[[package]]
name = "reactive_stores_macro"
version = "0.2.0-beta"
version = "0.2.2"
dependencies = [
"convert_case 0.7.1",
"convert_case 0.8.0",
"proc-macro-error2",
"proc-macro2",
"quote",
@@ -2835,9 +2865,9 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.12.14"
version = "0.12.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989e327e510263980e231de548a33e63d34962d29ae61b467389a1a09627a254"
checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
dependencies = [
"base64",
"bytes",
@@ -2992,9 +3022,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825"
checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96"
dependencies = [
"bitflags",
"errno",
@@ -3005,9 +3035,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.23"
version = "0.23.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395"
checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c"
dependencies = [
"once_cell",
"ring",
@@ -3037,9 +3067,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
version = "0.102.8"
version = "0.103.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
dependencies = [
"ring",
"rustls-pki-types",
@@ -3067,6 +3097,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "scc"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea091f6cac2595aa38993f04f4ee692ed43757035c36e67c180b6828356385b1"
dependencies = [
"sdd",
]
[[package]]
name = "schannel"
version = "0.1.27"
@@ -3082,6 +3121,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sdd"
version = "3.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21"
[[package]]
name = "security-framework"
version = "2.11.1"
@@ -3196,13 +3241,13 @@ dependencies = [
[[package]]
name = "serde_qs"
version = "0.13.0"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd34f36fe4c5ba9654417139a9b3a20d2e1de6012ee678ad14d240c22c78d8d6"
checksum = "8b417bedc008acbdf6d6b4bc482d29859924114bbe2650b7921fb68a261d0aa6"
dependencies = [
"percent-encoding",
"serde",
"thiserror 1.0.69",
"thiserror 2.0.12",
]
[[package]]
@@ -3226,9 +3271,34 @@ dependencies = [
"serde",
]
[[package]]
name = "serial_test"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9"
dependencies = [
"futures",
"log",
"once_cell",
"parking_lot",
"scc",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "server_fn"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"actix-web",
"actix-ws",
@@ -3253,6 +3323,8 @@ dependencies = [
"reqwest",
"rkyv",
"rmp-serde",
"rustc_version",
"rustversion",
"send_wrapper",
"serde",
"serde-lite",
@@ -3265,6 +3337,7 @@ dependencies = [
"tokio-tungstenite",
"tower",
"tower-layer",
"trybuild",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
@@ -3289,21 +3362,22 @@ dependencies = [
[[package]]
name = "server_fn_macro"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"const_format",
"convert_case 0.6.0",
"convert_case 0.8.0",
"proc-macro2",
"quote",
"rustc_version",
"syn 2.0.100",
"xxhash-rust",
]
[[package]]
name = "server_fn_macro_default"
version = "0.8.0-beta"
version = "0.8.2"
dependencies = [
"server_fn_macro 0.8.0-beta",
"server_fn_macro 0.8.2",
"syn 2.0.100",
]
@@ -3497,7 +3571,7 @@ dependencies = [
[[package]]
name = "tachys"
version = "0.2.0-beta"
version = "0.2.2"
dependencies = [
"any_spawner",
"async-trait",
@@ -3520,6 +3594,7 @@ dependencies = [
"reactive_graph",
"reactive_stores",
"rustc-hash 2.1.1",
"rustc_version",
"send_wrapper",
"serde",
"serde_json",
@@ -3558,13 +3633,12 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.18.0"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567"
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
dependencies = [
"cfg-if",
"fastrand",
"getrandom 0.3.1",
"getrandom 0.3.2",
"once_cell",
"rustix",
"windows-sys 0.59.0",
@@ -3628,9 +3702,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.39"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [
"deranged",
"itoa",
@@ -3643,15 +3717,15 @@ dependencies = [
[[package]]
name = "time-core"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]]
name = "time-macros"
version = "0.2.20"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c"
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
dependencies = [
"num-conv",
"time-core",
@@ -3684,9 +3758,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.44.0"
version = "1.44.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a"
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
dependencies = [
"backtrace",
"bytes",
@@ -3770,9 +3844,9 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.13"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
dependencies = [
"bytes",
"futures-core",
@@ -3940,18 +4014,38 @@ dependencies = [
[[package]]
name = "typed-builder"
version = "0.20.0"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e14ed59dc8b7b26cacb2a92bad2e8b1f098806063898ab42a3bd121d7d45e75"
checksum = "cd9d30e3a08026c78f246b173243cf07b3696d274debd26680773b6773c2afc7"
dependencies = [
"typed-builder-macro",
"typed-builder-macro 0.20.1",
]
[[package]]
name = "typed-builder"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce63bcaf7e9806c206f7d7b9c1f38e0dce8bb165a80af0898161058b19248534"
dependencies = [
"typed-builder-macro 0.21.0",
]
[[package]]
name = "typed-builder-macro"
version = "0.20.0"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5"
checksum = "3c36781cc0e46a83726d9879608e4cf6c2505237e263a8eb8c24502989cfdb28"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "typed-builder-macro"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60d8d828da2a3d759d3519cdf29a5bac49c77d039ad36d0782edadbf9cd5415b"
dependencies = [
"proc-macro2",
"quote",
@@ -4031,11 +4125,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.15.1"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
dependencies = [
"getrandom 0.3.1",
"getrandom 0.3.2",
]
[[package]]
@@ -4089,9 +4183,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
@@ -4167,6 +4261,30 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-bindgen-test"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3"
dependencies = [
"js-sys",
"minicov",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "wasm-streams"
version = "0.4.2"
@@ -4220,9 +4338,9 @@ dependencies = [
[[package]]
name = "windows-link"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-registry"
@@ -4237,9 +4355,9 @@ dependencies = [
[[package]]
name = "windows-result"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189"
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
dependencies = [
"windows-link",
]
@@ -4410,9 +4528,9 @@ dependencies = [
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
@@ -4467,18 +4585,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.23"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6"
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.23"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154"
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
dependencies = [
"proc-macro2",
"quote",
@@ -4545,18 +4663,18 @@ dependencies = [
[[package]]
name = "zstd-safe"
version = "7.2.3"
version = "7.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3051792fbdc2e1e143244dc28c60f73d8470e93f3f9cbd0ead44da5ed802722"
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.14+zstd.1.5.7"
version = "2.0.15+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5"
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
dependencies = [
"cc",
"pkg-config",

View File

@@ -40,39 +40,44 @@ members = [
exclude = ["benchmarks", "examples", "projects"]
[workspace.package]
version = "0.8.0-beta"
version = "0.8.2"
edition = "2021"
rust-version = "1.76"
[workspace.dependencies]
convert_case = "0.8"
throw_error = { path = "./any_error/", version = "0.3.0" }
any_spawner = { path = "./any_spawner/", version = "0.2.0" }
any_spawner = { path = "./any_spawner/", version = "0.3.0" }
const_str_slice_concat = { path = "./const_str_slice_concat", version = "0.1" }
either_of = { path = "./either_of/", version = "0.1.5" }
hydration_context = { path = "./hydration_context", version = "0.3.0" }
itertools = "0.14.0"
leptos = { path = "./leptos", version = "0.8.0-beta" }
leptos_config = { path = "./leptos_config", version = "0.8.0-beta" }
leptos_dom = { path = "./leptos_dom", version = "0.8.0-beta" }
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.8.0-beta" }
leptos_integration_utils = { path = "./integrations/utils", version = "0.8.0-beta" }
leptos_macro = { path = "./leptos_macro", version = "0.8.0-beta" }
leptos_router = { path = "./router", version = "0.8.0-beta" }
leptos_router_macro = { path = "./router_macro", version = "0.8.0-beta" }
leptos_server = { path = "./leptos_server", version = "0.8.0-beta" }
leptos_meta = { path = "./meta", version = "0.8.0-beta" }
leptos = { path = "./leptos", version = "0.8.2" }
leptos_config = { path = "./leptos_config", version = "0.8.2" }
leptos_dom = { path = "./leptos_dom", version = "0.8.2" }
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.8.2" }
leptos_integration_utils = { path = "./integrations/utils", version = "0.8.2" }
leptos_macro = { path = "./leptos_macro", version = "0.8.2" }
leptos_router = { path = "./router", version = "0.8.2" }
leptos_router_macro = { path = "./router_macro", version = "0.8.2" }
leptos_server = { path = "./leptos_server", version = "0.8.2" }
leptos_meta = { path = "./meta", version = "0.8.2" }
next_tuple = { path = "./next_tuple", version = "0.1.0" }
oco_ref = { path = "./oco", version = "0.2.0" }
or_poisoned = { path = "./or_poisoned", version = "0.1.0" }
reactive_graph = { path = "./reactive_graph", version = "0.2.0-beta" }
reactive_stores = { path = "./reactive_stores", version = "0.2.0-beta" }
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.2.0-beta" }
reactive_graph = { path = "./reactive_graph", version = "0.2.0" }
reactive_stores = { path = "./reactive_stores", version = "0.2.0" }
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.2.0" }
rustversion = "1"
serde_json = "1.0.0"
server_fn = { path = "./server_fn", version = "0.8.0-beta" }
server_fn_macro = { path = "./server_fn_macro", version = "0.8.0-beta" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.8.0-beta" }
tachys = { path = "./tachys", version = "0.2.0-beta" }
wasm-bindgen = { version = "0.2.100" }
server_fn = { path = "./server_fn", version = "0.8.2" }
server_fn_macro = { path = "./server_fn_macro", version = "0.8.2" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.8.2" }
tachys = { path = "./tachys", version = "0.2.0" }
trybuild = "1"
typed-builder = "0.21.0"
thiserror = "2.0.12"
wasm-bindgen = "0.2.100"
[profile.release]
codegen-units = 1
@@ -81,6 +86,7 @@ opt-level = 'z'
[workspace.metadata.cargo-all-features]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
max_combination_size = 2
[workspace.lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = [

View File

@@ -10,8 +10,8 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
workspace = false
clear = true
dependencies = [
{ name = "check", path = "examples/counter_without_macros" },
{ name = "check", path = "examples/counters_stable" },
{ name = "lint", path = "examples/counter_without_macros" },
{ name = "lint", path = "examples/counters_stable" },
]
[tasks.ci-examples]

View File

@@ -1,6 +1,6 @@
[package]
name = "any_spawner"
version = "0.2.1"
version = "0.3.0"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"
@@ -12,13 +12,23 @@ edition.workspace = true
async-executor = { version = "1.13.1", optional = true }
futures = "0.3.31"
glib = { version = "0.20.6", optional = true }
thiserror = "2.0"
thiserror = { workspace = true }
tokio = { version = "1.41", optional = true, default-features = false, features = [
"rt",
] }
tracing = { version = "0.1.41", optional = true }
wasm-bindgen-futures = { version = "0.4.50", optional = true }
[dev-dependencies]
futures-lite = { version = "2.6.0", default-features = false }
tokio = { version = "1.41", default-features = false, features = [
"rt",
"macros",
"time",
] }
wasm-bindgen-test = { version = "0.3.50" }
serial_test = "3.2.0"
[features]
async-executor = ["dep:async-executor"]
tracing = ["dep:tracing"]
@@ -34,3 +44,4 @@ rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.cargo-all-features]
denylist = ["tracing"]
max_combination_size = 2

View File

@@ -1 +1,4 @@
extend = { path = "../cargo-make/main.toml" }
extend = [
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/wasm-test.toml" },
]

View File

@@ -11,18 +11,16 @@
//! - no "join handle" or other result is returned from the spawn
//! - the `Future` must output `()`
//!
//! ```rust
//! ```no_run
//! use any_spawner::Executor;
//!
//! // make sure an Executor has been initialized with one of the init_ functions
//!
//! # if false {
//! // spawn a thread-safe Future
//! Executor::spawn(async { /* ... */ });
//!
//! // spawn a Future that is !Send
//! Executor::spawn_local(async { /* ... */ });
//! # }
//! ```
#![forbid(unsafe_code)]
@@ -37,15 +35,67 @@ pub type PinnedFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
/// A future that has been pinned.
pub type PinnedLocalFuture<T> = Pin<Box<dyn Future<Output = T>>>;
static SPAWN: OnceLock<fn(PinnedFuture<()>)> = OnceLock::new();
static SPAWN_LOCAL: OnceLock<fn(PinnedLocalFuture<()>)> = OnceLock::new();
static POLL_LOCAL: OnceLock<fn()> = OnceLock::new();
// Type alias for the spawn function pointer.
type SpawnFn = fn(PinnedFuture<()>);
// Type alias for the spawn_local function pointer.
type SpawnLocalFn = fn(PinnedLocalFuture<()>);
// Type alias for the poll_local function pointer.
type PollLocalFn = fn();
/// Holds the function pointers for the current global executor.
#[derive(Clone, Copy)]
struct ExecutorFns {
spawn: SpawnFn,
spawn_local: SpawnLocalFn,
poll_local: PollLocalFn,
}
// Use a single OnceLock to ensure atomic initialization of all functions.
static EXECUTOR_FNS: OnceLock<ExecutorFns> = OnceLock::new();
// No-op functions to use when an executor doesn't support a specific operation.
#[cfg(any(feature = "tokio", feature = "wasm-bindgen", feature = "glib"))]
#[cold]
#[inline(never)]
fn no_op_poll() {}
#[cfg(all(not(feature = "wasm-bindgen"), not(debug_assertions)))]
#[cold]
#[inline(never)]
fn no_op_spawn(_: PinnedFuture<()>) {
#[cfg(debug_assertions)]
eprintln!(
"Warning: Executor::spawn called, but no global 'spawn' function is \
configured (perhaps only spawn_local is supported, e.g., on wasm \
without threading?)."
);
}
// Wasm panics if you spawn without an executor
#[cfg(feature = "wasm-bindgen")]
#[cold]
#[inline(never)]
fn no_op_spawn(_: PinnedFuture<()>) {
panic!(
"Executor::spawn called, but no global 'spawn' function is configured."
);
}
#[cfg(not(debug_assertions))]
#[cold]
#[inline(never)]
fn no_op_spawn_local(_: PinnedLocalFuture<()>) {
panic!(
"Executor::spawn_local called, but no global 'spawn_local' function \
is configured."
);
}
/// Errors that can occur when using the executor.
#[derive(Error, Debug)]
pub enum ExecutorError {
/// The executor has already been set.
#[error("Executor has already been set.")]
#[error("Global executor has already been set.")]
AlreadySet,
}
@@ -54,150 +104,143 @@ pub struct Executor;
impl Executor {
/// Spawns a thread-safe [`Future`].
/// ```rust
/// use any_spawner::Executor;
/// # if false {
/// // spawn a thread-safe Future
/// Executor::spawn(async { /* ... */ });
/// # }
/// ```
///
/// Uses the globally configured executor.
/// Panics if no global executor has been initialized.
#[inline(always)]
#[track_caller]
pub fn spawn(fut: impl Future<Output = ()> + Send + 'static) {
if let Some(spawner) = SPAWN.get() {
spawner(Box::pin(fut))
let pinned_fut = Box::pin(fut);
if let Some(fns) = EXECUTOR_FNS.get() {
(fns.spawn)(pinned_fut)
} else {
#[cfg(all(debug_assertions, feature = "tracing"))]
tracing::error!(
"At {}, tried to spawn a Future with Executor::spawn() before \
the Executor had been set.",
std::panic::Location::caller()
);
#[cfg(all(debug_assertions, not(feature = "tracing")))]
panic!(
"At {}, tried to spawn a Future with Executor::spawn() before \
the Executor had been set.",
std::panic::Location::caller()
);
// No global executor set.
handle_uninitialized_spawn(pinned_fut);
}
}
/// Spawns a [`Future`] that cannot be sent across threads.
/// ```rust
/// use any_spawner::Executor;
///
/// # if false {
/// // spawn a thread-safe Future
/// Executor::spawn_local(async { /* ... */ });
/// # }
/// ```
/// Uses the globally configured executor.
/// Panics if no global executor has been initialized.
#[inline(always)]
#[track_caller]
pub fn spawn_local(fut: impl Future<Output = ()> + 'static) {
if let Some(spawner) = SPAWN_LOCAL.get() {
spawner(Box::pin(fut))
let pinned_fut = Box::pin(fut);
if let Some(fns) = EXECUTOR_FNS.get() {
(fns.spawn_local)(pinned_fut)
} else {
#[cfg(all(debug_assertions, feature = "tracing"))]
tracing::error!(
"At {}, tried to spawn a Future with Executor::spawn_local() \
before the Executor had been set.",
std::panic::Location::caller()
);
#[cfg(all(debug_assertions, not(feature = "tracing")))]
panic!(
"At {}, tried to spawn a Future with Executor::spawn_local() \
before the Executor had been set.",
std::panic::Location::caller()
);
// No global executor set.
handle_uninitialized_spawn_local(pinned_fut);
}
}
/// Waits until the next "tick" of the current async executor.
/// Respects the global executor.
#[inline(always)]
pub async fn tick() {
let (tx, rx) = futures::channel::oneshot::channel();
#[cfg(not(all(feature = "wasm-bindgen", target_family = "wasm")))]
Executor::spawn(async move {
_ = tx.send(());
});
#[cfg(all(feature = "wasm-bindgen", target_family = "wasm"))]
Executor::spawn_local(async move {
_ = tx.send(());
});
_ = rx.await;
}
/// Polls the current async executor.
/// Not all async executors support polling, so this function may not do anything.
/// Polls the global async executor.
///
/// Uses the globally configured executor.
/// Does nothing if the global executor does not support polling.
#[inline(always)]
pub fn poll_local() {
if let Some(poller) = POLL_LOCAL.get() {
poller()
if let Some(fns) = EXECUTOR_FNS.get() {
(fns.poll_local)()
}
// If not initialized or doesn't support polling, do nothing gracefully.
}
}
impl Executor {
/// Globally sets the [`tokio`] runtime as the executor used to spawn tasks.
///
/// Returns `Err(_)` if an executor has already been set.
/// Returns `Err(_)` if a global executor has already been set.
///
/// Requires the `tokio` feature to be activated on this crate.
#[cfg(feature = "tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
pub fn init_tokio() -> Result<(), ExecutorError> {
SPAWN
.set(|fut| {
let executor_impl = ExecutorFns {
spawn: |fut| {
tokio::spawn(fut);
})
.map_err(|_| ExecutorError::AlreadySet)?;
SPAWN_LOCAL
.set(|fut| {
},
spawn_local: |fut| {
tokio::task::spawn_local(fut);
})
.map_err(|_| ExecutorError::AlreadySet)?;
Ok(())
},
// Tokio doesn't have an explicit global poll function like LocalPool::run_until_stalled
poll_local: no_op_poll,
};
EXECUTOR_FNS
.set(executor_impl)
.map_err(|_| ExecutorError::AlreadySet)
}
/// Globally sets the [`wasm-bindgen-futures`] runtime as the executor used to spawn tasks.
///
/// Returns `Err(_)` if an executor has already been set.
/// Returns `Err(_)` if a global executor has already been set.
///
/// Requires the `wasm-bindgen` feature to be activated on this crate.
#[cfg(feature = "wasm-bindgen")]
#[cfg_attr(docsrs, doc(cfg(feature = "wasm-bindgen")))]
pub fn init_wasm_bindgen() -> Result<(), ExecutorError> {
SPAWN
.set(|fut| {
let executor_impl = ExecutorFns {
// wasm-bindgen-futures only supports spawn_local
spawn: no_op_spawn,
spawn_local: |fut| {
wasm_bindgen_futures::spawn_local(fut);
})
.map_err(|_| ExecutorError::AlreadySet)?;
SPAWN_LOCAL
.set(|fut| {
wasm_bindgen_futures::spawn_local(fut);
})
.map_err(|_| ExecutorError::AlreadySet)?;
Ok(())
},
poll_local: no_op_poll,
};
EXECUTOR_FNS
.set(executor_impl)
.map_err(|_| ExecutorError::AlreadySet)
}
/// Globally sets the [`glib`] runtime as the executor used to spawn tasks.
///
/// Returns `Err(_)` if an executor has already been set.
/// Returns `Err(_)` if a global executor has already been set.
///
/// Requires the `glib` feature to be activated on this crate.
#[cfg(feature = "glib")]
#[cfg_attr(docsrs, doc(cfg(feature = "glib")))]
pub fn init_glib() -> Result<(), ExecutorError> {
SPAWN
.set(|fut| {
let executor_impl = ExecutorFns {
spawn: |fut| {
let main_context = glib::MainContext::default();
main_context.spawn(fut);
})
.map_err(|_| ExecutorError::AlreadySet)?;
SPAWN_LOCAL
.set(|fut| {
},
spawn_local: |fut| {
let main_context = glib::MainContext::default();
main_context.spawn_local(fut);
})
.map_err(|_| ExecutorError::AlreadySet)?;
Ok(())
},
// Glib needs event loop integration, explicit polling isn't the standard model here.
poll_local: no_op_poll,
};
EXECUTOR_FNS
.set(executor_impl)
.map_err(|_| ExecutorError::AlreadySet)
}
/// Globally sets the [`futures`] executor as the executor used to spawn tasks,
/// lazily creating a thread pool to spawn tasks into.
///
/// Returns `Err(_)` if an executor has already been set.
/// Returns `Err(_)` if a global executor has already been set.
///
/// Requires the `futures-executor` feature to be activated on this crate.
#[cfg(feature = "futures-executor")]
@@ -209,9 +252,11 @@ impl Executor {
};
use std::cell::RefCell;
// Keep the lazy-init ThreadPool and thread-local LocalPool for spawn_local impl
static THREAD_POOL: OnceLock<ThreadPool> = OnceLock::new();
thread_local! {
static LOCAL_POOL: RefCell<LocalPool> = RefCell::new(LocalPool::new());
// SPAWNER is derived from LOCAL_POOL, keep it for efficiency inside the closure
static SPAWNER: LocalSpawner = LOCAL_POOL.with(|pool| pool.borrow().spawner());
}
@@ -222,140 +267,248 @@ impl Executor {
})
}
SPAWN
.set(|fut| {
let executor_impl = ExecutorFns {
spawn: |fut| {
get_thread_pool()
.spawn(fut)
.expect("failed to spawn future");
})
.map_err(|_| ExecutorError::AlreadySet)?;
SPAWN_LOCAL
.set(|fut| {
.expect("failed to spawn future on ThreadPool");
},
spawn_local: |fut| {
// Use the thread_local SPAWNER derived from LOCAL_POOL
SPAWNER.with(|spawner| {
spawner.spawn_local(fut).expect("failed to spawn future");
spawner
.spawn_local(fut)
.expect("failed to spawn local future");
});
})
.map_err(|_| ExecutorError::AlreadySet)?;
POLL_LOCAL
.set(|| {
},
poll_local: || {
// Use the thread_local LOCAL_POOL
LOCAL_POOL.with(|pool| {
// Use try_borrow_mut to prevent panic during re-entrant calls
if let Ok(mut pool) = pool.try_borrow_mut() {
pool.run_until_stalled();
}
// If we couldn't borrow_mut, we're in a nested call to poll, so we don't need to do anything.
// If already borrowed, we're likely in a nested poll, so do nothing.
});
})
.map_err(|_| ExecutorError::AlreadySet)?;
Ok(())
},
};
EXECUTOR_FNS
.set(executor_impl)
.map_err(|_| ExecutorError::AlreadySet)
}
/// Globally sets the [`async_executor`] executor as the executor used to spawn tasks,
/// lazily creating a thread pool to spawn tasks into.
///
/// Returns `Err(_)` if an executor has already been set.
/// Returns `Err(_)` if a global executor has already been set.
///
/// Requires the `async-executor` feature to be activated on this crate.
#[cfg(feature = "async-executor")]
#[cfg_attr(docsrs, doc(cfg(feature = "async-executor")))]
pub fn init_async_executor() -> Result<(), ExecutorError> {
use async_executor::{Executor, LocalExecutor};
use async_executor::{Executor as AsyncExecutor, LocalExecutor};
static THREAD_POOL: OnceLock<Executor> = OnceLock::new();
// Keep the lazy-init global Executor and thread-local LocalExecutor for spawn_local impl
static ASYNC_EXECUTOR: OnceLock<AsyncExecutor<'static>> =
OnceLock::new();
thread_local! {
static LOCAL_POOL: LocalExecutor<'static> = const { LocalExecutor::new() };
static LOCAL_EXECUTOR_POOL: LocalExecutor<'static> = const { LocalExecutor::new() };
}
fn get_thread_pool() -> &'static Executor<'static> {
THREAD_POOL.get_or_init(Executor::new)
fn get_async_executor() -> &'static AsyncExecutor<'static> {
ASYNC_EXECUTOR.get_or_init(AsyncExecutor::new)
}
SPAWN
.set(|fut| {
get_thread_pool().spawn(fut).detach();
})
.map_err(|_| ExecutorError::AlreadySet)?;
SPAWN_LOCAL
.set(|fut| {
LOCAL_POOL.with(|pool| pool.spawn(fut).detach());
})
.map_err(|_| ExecutorError::AlreadySet)?;
POLL_LOCAL
.set(|| {
LOCAL_POOL.with(|pool| pool.try_tick());
})
.map_err(|_| ExecutorError::AlreadySet)?;
Ok(())
let executor_impl = ExecutorFns {
spawn: |fut| {
get_async_executor().spawn(fut).detach();
},
spawn_local: |fut| {
LOCAL_EXECUTOR_POOL.with(|pool| pool.spawn(fut).detach());
},
poll_local: || {
LOCAL_EXECUTOR_POOL.with(|pool| {
// try_tick polls the local executor without blocking
// This prevents issues if called recursively or from within a task.
pool.try_tick();
});
},
};
EXECUTOR_FNS
.set(executor_impl)
.map_err(|_| ExecutorError::AlreadySet)
}
/// Globally sets a custom executor as the executor used to spawn tasks.
///
/// Returns `Err(_)` if an executor has already been set.
/// Requires the custom executor to be `Send + Sync` as it will be stored statically.
///
/// Returns `Err(_)` if a global executor has already been set.
pub fn init_custom_executor(
custom_executor: impl CustomExecutor + Send + Sync + 'static,
) -> Result<(), ExecutorError> {
static EXECUTOR: OnceLock<Box<dyn CustomExecutor + Send + Sync>> =
OnceLock::new();
EXECUTOR
// Store the custom executor instance itself to call its methods.
// Use Box for dynamic dispatch.
static CUSTOM_EXECUTOR_INSTANCE: OnceLock<
Box<dyn CustomExecutor + Send + Sync>,
> = OnceLock::new();
CUSTOM_EXECUTOR_INSTANCE
.set(Box::new(custom_executor))
.map_err(|_| ExecutorError::AlreadySet)?;
SPAWN
.set(|fut| {
EXECUTOR.get().unwrap().spawn(fut);
})
.map_err(|_| ExecutorError::AlreadySet)?;
SPAWN_LOCAL
.set(|fut| EXECUTOR.get().unwrap().spawn_local(fut))
.map_err(|_| ExecutorError::AlreadySet)?;
POLL_LOCAL
.set(|| EXECUTOR.get().unwrap().poll_local())
.map_err(|_| ExecutorError::AlreadySet)?;
Ok(())
// Now set the ExecutorFns using the stored instance
let executor_impl = ExecutorFns {
spawn: |fut| {
// Unwrap is safe because we just set it successfully or returned Err.
CUSTOM_EXECUTOR_INSTANCE.get().unwrap().spawn(fut);
},
spawn_local: |fut| {
CUSTOM_EXECUTOR_INSTANCE.get().unwrap().spawn_local(fut);
},
poll_local: || {
CUSTOM_EXECUTOR_INSTANCE.get().unwrap().poll_local();
},
};
EXECUTOR_FNS
.set(executor_impl)
.map_err(|_| ExecutorError::AlreadySet)
// If setting EXECUTOR_FNS fails (extremely unlikely race if called *concurrently*
// with another init_* after CUSTOM_EXECUTOR_INSTANCE was set), we technically
// leave CUSTOM_EXECUTOR_INSTANCE set but EXECUTOR_FNS not. This is an edge case,
// but the primary race condition is solved.
}
/// Locally sets a custom executor as the executor used to spawn tasks
/// in the current thread.
/// Sets a custom executor *for the current thread only*.
///
/// Returns `Err(_)` if an executor has already been set.
/// This overrides the global executor for calls to `spawn`, `spawn_local`, and `poll_local`
/// made *from the current thread*. It does not affect other threads or the global state.
///
/// The provided `custom_executor` must implement [`CustomExecutor`] and `'static`, but does
/// **not** need to be `Send` or `Sync`.
///
/// Returns `Err(ExecutorError::AlreadySet)` if a *local* executor has already been set
/// *for this thread*.
pub fn init_local_custom_executor(
custom_executor: impl CustomExecutor + 'static,
) -> Result<(), ExecutorError> {
// Store the custom executor instance itself to call its methods.
// Use Box for dynamic dispatch.
thread_local! {
static EXECUTOR: OnceLock<Box<dyn CustomExecutor>> = OnceLock::new();
}
EXECUTOR.with(|this| {
static CUSTOM_EXECUTOR_INSTANCE: OnceLock<
Box<dyn CustomExecutor>,
> = OnceLock::new();
};
CUSTOM_EXECUTOR_INSTANCE.with(|this| {
this.set(Box::new(custom_executor))
.map_err(|_| ExecutorError::AlreadySet)
})?;
SPAWN
.set(|fut| {
EXECUTOR.with(|this| this.get().unwrap().spawn(fut));
})
.map_err(|_| ExecutorError::AlreadySet)?;
SPAWN_LOCAL
.set(|fut| {
EXECUTOR.with(|this| this.get().unwrap().spawn_local(fut));
})
.map_err(|_| ExecutorError::AlreadySet)?;
POLL_LOCAL
.set(|| {
EXECUTOR.with(|this| this.get().unwrap().poll_local());
})
.map_err(|_| ExecutorError::AlreadySet)?;
Ok(())
// Now set the ExecutorFns using the stored instance
let executor_impl = ExecutorFns {
spawn: |fut| {
// Unwrap is safe because we just set it successfully or returned Err.
CUSTOM_EXECUTOR_INSTANCE
.with(|this| this.get().unwrap().spawn(fut));
},
spawn_local: |fut| {
CUSTOM_EXECUTOR_INSTANCE
.with(|this| this.get().unwrap().spawn_local(fut));
},
poll_local: || {
CUSTOM_EXECUTOR_INSTANCE
.with(|this| this.get().unwrap().poll_local());
},
};
EXECUTOR_FNS
.set(executor_impl)
.map_err(|_| ExecutorError::AlreadySet)
}
}
/// A trait for custom executors.
/// Custom executors can be used to integrate with any executor that supports spawning futures.
///
/// All methods can be called recursively.
///
/// If used with `init_custom_executor`, the implementation must be `Send + Sync + 'static`.
///
/// All methods can be called recursively. Implementors should be mindful of potential
/// deadlocks or excessive resource consumption if recursive calls are not handled carefully
/// (e.g., using `try_borrow_mut` or non-blocking polls within implementations).
pub trait CustomExecutor {
/// Spawns a future, usually on a thread pool.
fn spawn(&self, fut: PinnedFuture<()>);
/// Spawns a local future. May require calling `poll_local` to make progress.
fn spawn_local(&self, fut: PinnedLocalFuture<()>);
/// Polls the executor, if it supports polling.
/// Polls the executor, if it supports polling. Implementations should ideally be
/// non-blocking or use mechanisms like `try_tick` or `try_borrow_mut` to handle
/// re-entrant calls safely.
fn poll_local(&self);
}
// Ensure CustomExecutor is object-safe
#[allow(dead_code)]
fn test_object_safety(_: Box<dyn CustomExecutor + Send + Sync>) {} // Added Send + Sync constraint here for global usage
/// Handles the case where `Executor::spawn` is called without an initialized executor.
#[cold] // Less likely path
#[inline(never)]
#[track_caller]
fn handle_uninitialized_spawn(_fut: PinnedFuture<()>) {
let caller = std::panic::Location::caller();
#[cfg(all(debug_assertions, feature = "tracing"))]
{
tracing::error!(
target: "any_spawner",
spawn_caller=%caller,
"Executor::spawn called before a global executor was initialized. Task dropped."
);
// Drop the future implicitly after logging
drop(_fut);
}
#[cfg(all(debug_assertions, not(feature = "tracing")))]
{
panic!(
"At {caller}, tried to spawn a Future with Executor::spawn() \
before a global executor was initialized."
);
}
// In release builds (without tracing), call the specific no-op function.
#[cfg(not(debug_assertions))]
{
no_op_spawn(_fut);
}
}
/// Handles the case where `Executor::spawn_local` is called without an initialized executor.
#[cold] // Less likely path
#[inline(never)]
#[track_caller]
fn handle_uninitialized_spawn_local(_fut: PinnedLocalFuture<()>) {
let caller = std::panic::Location::caller();
#[cfg(all(debug_assertions, feature = "tracing"))]
{
tracing::error!(
target: "any_spawner",
spawn_caller=%caller,
"Executor::spawn_local called before a global executor was initialized. \
Task likely dropped or panicked."
);
// Fall through to panic or no-op depending on build/target
}
#[cfg(all(debug_assertions, not(feature = "tracing")))]
{
panic!(
"At {caller}, tried to spawn a Future with \
Executor::spawn_local() before a global executor was initialized."
);
}
// In release builds (without tracing), call the specific no-op function (which usually panics).
#[cfg(not(debug_assertions))]
{
no_op_spawn_local(_fut);
}
}

View File

@@ -0,0 +1,24 @@
use any_spawner::{Executor, ExecutorError};
#[test]
fn test_already_set_error() {
struct SimpleExecutor;
impl any_spawner::CustomExecutor for SimpleExecutor {
fn spawn(&self, _fut: any_spawner::PinnedFuture<()>) {}
fn spawn_local(&self, _fut: any_spawner::PinnedLocalFuture<()>) {}
fn poll_local(&self) {}
}
// First initialization should succeed
Executor::init_custom_executor(SimpleExecutor)
.expect("First initialization failed");
// Second initialization should fail with AlreadySet error
let result = Executor::init_custom_executor(SimpleExecutor);
assert!(matches!(result, Err(ExecutorError::AlreadySet)));
// First local initialization should fail
let result = Executor::init_local_custom_executor(SimpleExecutor);
assert!(matches!(result, Err(ExecutorError::AlreadySet)));
}

View File

@@ -0,0 +1,74 @@
#![cfg(feature = "async-executor")]
use std::{
future::Future,
pin::Pin,
sync::{Arc, Mutex},
};
// A simple async executor for testing
struct TestExecutor {
tasks: Mutex<Vec<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>>,
}
impl TestExecutor {
fn new() -> Self {
TestExecutor {
tasks: Mutex::new(Vec::new()),
}
}
fn spawn<F>(&self, future: F)
where
F: Future<Output = ()> + Send + 'static,
{
self.tasks.lock().unwrap().push(Box::pin(future));
}
fn run_all(&self) {
// Take all tasks out to process them
let tasks = self.tasks.lock().unwrap().drain(..).collect::<Vec<_>>();
// Use a basic future executor to run each task to completion
for mut task in tasks {
// Use futures-lite's block_on to complete the future
futures::executor::block_on(async {
unsafe {
let task_mut = Pin::new_unchecked(&mut task);
let _ = std::future::Future::poll(
task_mut,
&mut std::task::Context::from_waker(
futures::task::noop_waker_ref(),
),
);
}
});
}
}
}
#[test]
fn test_async_executor() {
let executor = Arc::new(TestExecutor::new());
let executor_clone = executor.clone();
// Create a spawner function that will use our test executor
let spawner = move |future| {
executor_clone.spawn(future);
};
// Prepare test data
let counter = Arc::new(Mutex::new(0));
let counter_clone = counter.clone();
// Use the spawner to spawn a task
spawner(async move {
*counter_clone.lock().unwrap() += 1;
});
// Run all tasks
executor.run_all();
// Check if the task completed correctly
assert_eq!(*counter.lock().unwrap(), 1);
}

View File

@@ -0,0 +1,63 @@
use any_spawner::Executor;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
#[test]
fn test_custom_executor() {
// Define a simple custom executor
struct TestExecutor {
spawn_called: Arc<AtomicBool>,
spawn_local_called: Arc<AtomicBool>,
poll_local_called: Arc<AtomicBool>,
}
impl any_spawner::CustomExecutor for TestExecutor {
fn spawn(&self, fut: any_spawner::PinnedFuture<()>) {
self.spawn_called.store(true, Ordering::SeqCst);
// Execute the future immediately (this works for simple test futures)
futures::executor::block_on(fut);
}
fn spawn_local(&self, fut: any_spawner::PinnedLocalFuture<()>) {
self.spawn_local_called.store(true, Ordering::SeqCst);
// Execute the future immediately
futures::executor::block_on(fut);
}
fn poll_local(&self) {
self.poll_local_called.store(true, Ordering::SeqCst);
}
}
let spawn_called = Arc::new(AtomicBool::new(false));
let spawn_local_called = Arc::new(AtomicBool::new(false));
let poll_local_called = Arc::new(AtomicBool::new(false));
let executor = TestExecutor {
spawn_called: spawn_called.clone(),
spawn_local_called: spawn_local_called.clone(),
poll_local_called: poll_local_called.clone(),
};
// Initialize with our custom executor
Executor::init_custom_executor(executor)
.expect("Failed to initialize custom executor");
// Test spawn
Executor::spawn(async {
// Simple task
});
assert!(spawn_called.load(Ordering::SeqCst));
// Test spawn_local
Executor::spawn_local(async {
// Simple local task
});
assert!(spawn_local_called.load(Ordering::SeqCst));
// Test poll_local
Executor::poll_local();
assert!(poll_local_called.load(Ordering::SeqCst));
}

View File

@@ -1,6 +1,7 @@
#[cfg(feature = "futures-executor")]
#![cfg(feature = "futures-executor")]
use any_spawner::{CustomExecutor, Executor, PinnedFuture, PinnedLocalFuture};
#[cfg(feature = "futures-executor")]
#[test]
fn can_create_custom_executor() {
use futures::{

View File

@@ -0,0 +1,28 @@
#![cfg(feature = "tokio")]
use any_spawner::Executor;
use std::{
sync::{Arc, Mutex},
time::Duration,
};
#[tokio::test]
async fn test_executor_tick() {
// Initialize the tokio executor
Executor::init_tokio().expect("Failed to initialize tokio executor");
let value = Arc::new(Mutex::new(false));
let value_clone = value.clone();
// Spawn a task that sets the value after a tick
Executor::spawn(async move {
Executor::tick().await;
*value_clone.lock().unwrap() = true;
});
// Allow some time for the task to complete
tokio::time::sleep(Duration::from_millis(50)).await;
// Check that the value was set
assert!(*value.lock().unwrap());
}

View File

@@ -0,0 +1,44 @@
#![cfg(feature = "futures-executor")]
use any_spawner::Executor;
use futures::channel::oneshot;
use std::{
sync::{Arc, Mutex},
time::Duration,
};
#[test]
fn test_futures_executor() {
// Initialize the futures executor
Executor::init_futures_executor()
.expect("Failed to initialize futures executor");
let (tx, rx) = oneshot::channel();
let result = Arc::new(Mutex::new(None));
let result_clone = result.clone();
// Spawn a task
Executor::spawn(async move {
tx.send(84).expect("Failed to send value");
});
// Spawn a task that waits for the result
Executor::spawn(async move {
match rx.await {
Ok(val) => *result_clone.lock().unwrap() = Some(val),
Err(_) => panic!("Failed to receive value"),
}
});
// Poll a few times to ensure the task completes
for _ in 0..10 {
Executor::poll_local();
std::thread::sleep(Duration::from_millis(10));
if result.lock().unwrap().is_some() {
break;
}
}
assert_eq!(*result.lock().unwrap(), Some(84));
}

View File

@@ -1,8 +1,8 @@
#[cfg(feature = "futures-executor")]
#![cfg(feature = "futures-executor")]
use any_spawner::Executor;
// All tests in this file use the same executor.
#[cfg(feature = "futures-executor")]
#[test]
fn can_spawn_local_future() {
use std::rc::Rc;
@@ -15,7 +15,6 @@ fn can_spawn_local_future() {
Executor::spawn(async {});
}
#[cfg(feature = "futures-executor")]
#[test]
fn can_make_local_progress() {
use std::sync::{

151
any_spawner/tests/glib.rs Normal file
View File

@@ -0,0 +1,151 @@
#![cfg(feature = "glib")]
use any_spawner::Executor;
use glib::{MainContext, MainLoop};
use serial_test::serial;
use std::{
cell::Cell,
future::Future,
rc::Rc,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
time::Duration,
};
// Helper to run a future to completion on a dedicated glib MainContext.
// Returns true if the future completed within the timeout, false otherwise.
fn run_on_glib_context<F>(fut: F)
where
F: Future<Output = ()> + Send + 'static,
{
let _ = Executor::init_glib();
let context = MainContext::default();
let main_loop = MainLoop::new(Some(&context), false);
let main_loop_clone = main_loop.clone();
Executor::spawn(async move {
fut.await;
main_loop_clone.quit();
});
main_loop.run();
}
// Helper to run a local (!Send) future on the glib context.
fn run_local_on_glib_context<F>(fut: F)
where
F: Future<Output = ()> + 'static,
{
let _ = Executor::init_glib();
let context = MainContext::default();
let main_loop = MainLoop::new(Some(&context), false);
let main_loop_clone = main_loop.clone();
Executor::spawn_local(async move {
fut.await;
main_loop_clone.quit();
});
main_loop.run();
}
// This test must run after a test that successfully initializes glib,
// or within its own process.
#[test]
#[serial]
fn test_glib_spawn() {
let success_flag = Arc::new(AtomicBool::new(false));
let flag_clone = success_flag.clone();
run_on_glib_context(async move {
// Simulate async work
futures_lite::future::yield_now().await;
flag_clone.store(true, Ordering::SeqCst);
// We need to give the spawned task time to run.
// The run_on_glib_context handles the main loop.
// We just need to ensure spawn happened correctly.
// Let's wait a tiny bit within the driving future to ensure spawn gets processed.
glib::timeout_future(Duration::from_millis(10)).await;
});
assert!(
success_flag.load(Ordering::SeqCst),
"Spawned future did not complete successfully"
);
}
// Similar conditions as test_glib_spawn regarding initialization state.
#[test]
#[serial]
fn test_glib_spawn_local() {
let success_flag = Rc::new(Cell::new(false));
let flag_clone = success_flag.clone();
run_local_on_glib_context(async move {
// Use Rc to make the future !Send
let non_send_data = Rc::new(Cell::new(10));
let data = non_send_data.get();
assert_eq!(data, 10, "Rc data should be accessible");
non_send_data.set(20); // Modify non-Send data
// Simulate async work
futures_lite::future::yield_now().await;
assert_eq!(
non_send_data.get(),
20,
"Rc data should persist modification"
);
flag_clone.set(true);
// Wait a tiny bit
glib::timeout_future(Duration::from_millis(10)).await;
});
assert!(
success_flag.get(),
"Spawned local future did not complete successfully"
);
}
// Test Executor::tick with glib backend
#[test]
#[serial]
fn test_glib_tick() {
run_on_glib_context(async {
let value = Arc::new(Mutex::new(false));
let value_clone = value.clone();
// Spawn a task that sets the value after a tick
Executor::spawn(async move {
Executor::tick().await;
*value_clone.lock().unwrap() = true;
});
// Allow some time for the task to complete
glib::timeout_future(Duration::from_millis(10)).await;
// Check that the value was set
assert!(*value.lock().unwrap());
});
}
// Test Executor::poll_local with glib backend (should be a no-op)
#[test]
#[serial]
fn test_glib_poll_local_is_no_op() {
// Ensure glib executor is initialized
let _ = Executor::init_glib();
// poll_local for glib is configured as a no-op
// Calling it should not panic or cause issues.
Executor::poll_local();
Executor::poll_local();
println!("Executor::poll_local called successfully (expected no-op).");
}

View File

@@ -0,0 +1,54 @@
use any_spawner::Executor;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
#[test]
fn test_local_custom_executor() {
// Define a thread-local custom executor
struct LocalTestExecutor {
spawn_called: Arc<AtomicBool>,
spawn_local_called: Arc<AtomicBool>,
}
impl any_spawner::CustomExecutor for LocalTestExecutor {
fn spawn(&self, fut: any_spawner::PinnedFuture<()>) {
self.spawn_called.store(true, Ordering::SeqCst);
futures::executor::block_on(fut);
}
fn spawn_local(&self, fut: any_spawner::PinnedLocalFuture<()>) {
self.spawn_local_called.store(true, Ordering::SeqCst);
futures::executor::block_on(fut);
}
fn poll_local(&self) {
// No-op for this test
}
}
let local_spawn_called = Arc::new(AtomicBool::new(false));
let local_spawn_local_called = Arc::new(AtomicBool::new(false));
let local_executor = LocalTestExecutor {
spawn_called: local_spawn_called.clone(),
spawn_local_called: local_spawn_local_called.clone(),
};
// Initialize a thread-local executor
Executor::init_local_custom_executor(local_executor)
.expect("Failed to initialize local custom executor");
// Test spawn - should use the thread-local executor
Executor::spawn(async {
// Simple task
});
assert!(local_spawn_called.load(Ordering::SeqCst));
// Test spawn_local - should use the thread-local executor
Executor::spawn_local(async {
// Simple local task
});
assert!(local_spawn_local_called.load(Ordering::SeqCst));
}

View File

@@ -0,0 +1,35 @@
#![cfg(feature = "tokio")]
use any_spawner::Executor;
use futures::channel::oneshot;
use std::sync::{Arc, Mutex};
#[tokio::test]
async fn test_multiple_tasks() {
Executor::init_tokio().expect("Failed to initialize tokio executor");
let counter = Arc::new(Mutex::new(0));
let tasks = 10;
let mut handles = Vec::new();
// Spawn multiple tasks that increment the counter
for _ in 0..tasks {
let counter_clone = counter.clone();
let (tx, rx) = oneshot::channel();
Executor::spawn(async move {
*counter_clone.lock().unwrap() += 1;
tx.send(()).expect("Failed to send completion signal");
});
handles.push(rx);
}
// Wait for all tasks to complete
for handle in handles {
handle.await.expect("Task failed");
}
// Verify that all tasks incremented the counter
assert_eq!(*counter.lock().unwrap(), tasks);
}

View File

@@ -0,0 +1,20 @@
#![cfg(feature = "tokio")]
use any_spawner::Executor;
use futures::channel::oneshot;
#[tokio::test]
async fn test_tokio_executor() {
// Initialize the tokio executor
Executor::init_tokio().expect("Failed to initialize tokio executor");
let (tx, rx) = oneshot::channel();
// Spawn a task that sends a value
Executor::spawn(async move {
tx.send(42).expect("Failed to send value");
});
// Wait for the spawned task to complete
assert_eq!(rx.await.unwrap(), 42);
}

View File

@@ -0,0 +1,88 @@
#![cfg(all(feature = "wasm-bindgen", target_family = "wasm"))]
use any_spawner::Executor;
use futures::channel::oneshot;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
async fn test_wasm_bindgen_spawn_local() {
// Initialize the wasm-bindgen executor
let _ = Executor::init_wasm_bindgen();
// Create a channel to verify the task completes
let (tx, rx) = oneshot::channel();
// Spawn a local task (wasm doesn't support sending futures between threads)
Executor::spawn_local(async move {
// Simulate some async work
Executor::tick().await;
tx.send(42).expect("Failed to send result");
});
// Wait for the task to complete
let result = rx.await.expect("Failed to receive result");
assert_eq!(result, 42);
}
#[wasm_bindgen_test]
async fn test_wasm_bindgen_tick() {
// Initialize the wasm-bindgen executor if not already initialized
let _ = Executor::init_wasm_bindgen();
let flag = Arc::new(AtomicBool::new(false));
let flag_clone = flag.clone();
// Spawn a task that will set the flag
Executor::spawn_local(async move {
flag_clone.store(true, Ordering::SeqCst);
});
// Wait for a tick, which should allow the spawned task to run
Executor::tick().await;
// Verify the flag was set
assert!(flag.load(Ordering::SeqCst));
}
#[wasm_bindgen_test]
async fn test_multiple_wasm_bindgen_tasks() {
// Initialize once for all tests
let _ = Executor::init_wasm_bindgen();
// Create channels for multiple tasks
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
// Spawn multiple tasks
Executor::spawn_local(async move {
tx1.send("task1").expect("Failed to send from task1");
});
Executor::spawn_local(async move {
tx2.send("task2").expect("Failed to send from task2");
});
// Wait for both tasks to complete
let (result1, result2) = futures::join!(rx1, rx2);
assert_eq!(result1.unwrap(), "task1");
assert_eq!(result2.unwrap(), "task2");
}
// This test verifies that spawn (not local) fails on wasm as expected
#[wasm_bindgen_test]
#[should_panic]
fn test_wasm_bindgen_spawn_errors() {
let _ = Executor::init_wasm_bindgen();
// Using should_panic to test that Executor::spawn panics in wasm
Executor::spawn(async {
// This should panic since wasm-bindgen doesn't support Send futures
});
}

View File

@@ -1,7 +0,0 @@
[tasks.check]
alias = "check-all"
[tasks.check-all]
command = "cargo"
args = ["check-all-features"]
install_crate = "cargo-all-features"

View File

@@ -6,13 +6,15 @@ env = { LEPTOS_PROJECT_DIRECTORY = "../" }
args = ["fmt", "--", "--check", "--config-path", "${LEPTOS_PROJECT_DIRECTORY}"]
[tasks.clippy-each-feature]
dependencies = ["install-clippy"]
command = "cargo"
args = [
"clippy",
"--all-features",
"--no-deps",
"--",
"-D",
"clippy::print_stdout",
"all-features",
"clippy",
"--no-deps",
"--",
"-D",
"clippy::print_stdout",
]
install_script = '''
cargo install --git https://github.com/sabify/cargo-all-features --branch arbitrary-command-support
'''

View File

@@ -1,8 +1,4 @@
extend = [
{ path = "./check.toml" },
{ path = "./lint.toml" },
{ path = "./test.toml" },
]
extend = [{ path = "./lint.toml" }, { path = "./test.toml" }]
[env]
RUSTFLAGS = ""
@@ -12,4 +8,4 @@ LEPTOS_OUTPUT_NAME = "ci" # allows examples to check/build without cargo-leptos
RUSTFLAGS = "-D warnings"
[tasks.ci]
dependencies = ["lint", "test"]
dependencies = ["lint", "test-each-feature", "doctests"]

View File

@@ -1,7 +1,16 @@
[tasks.test]
alias = "test-all"
[tasks.test-all]
[tasks.test-each-feature]
env = { "NEXTEST_NO_TESTS" = "warn" }
command = "cargo"
args = ["test-all-features"]
install_crate = "cargo-all-features"
args = ["all-features", "nextest", "run", "--all-targets"]
install_script = '''
cargo install --git https://github.com/sabify/cargo-all-features --branch arbitrary-command-support
'''
# This can be removed once doctests is supported in nextest
# https://github.com/nextest-rs/nextest/issues/16
[tasks.doctests]
command = "cargo"
args = ["all-features", "test", "--doc"]
install_script = '''
cargo install --git https://github.com/sabify/cargo-all-features --branch arbitrary-command-support
'''

View File

@@ -0,0 +1,7 @@
[tasks.post-test]
dependencies = ["test-wasm"]
[tasks.test-wasm]
env = { CARGO_MAKE_WASM_TEST_ARGS = "--headless --chrome --features=wasm-bindgen" }
command = "cargo"
args = ["make", "wasm-pack-test"]

View File

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

View File

@@ -776,20 +776,20 @@ tuples!(EitherOf16 + EitherOf16Future + EitherOf16FutureProj {
/// ```
#[macro_export]
macro_rules! either {
($match:expr, $left_pattern:pat => $left_expression:expr, $right_pattern:pat => $right_expression:expr,) => {
($match:expr, $left_pattern:pat => $left_expression:expr, $right_pattern:pat => $right_expression:expr$(,)?) => {
match $match {
$left_pattern => $crate::Either::Left($left_expression),
$right_pattern => $crate::Either::Right($right_expression),
}
};
($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr,) => {
($match:expr, $a_pattern:pat => $a_expression:expr, $b_pattern:pat => $b_expression:expr, $c_pattern:pat => $c_expression:expr$(,)?) => {
match $match {
$a_pattern => $crate::EitherOf3::A($a_expression),
$b_pattern => $crate::EitherOf3::B($b_expression),
$c_pattern => $crate::EitherOf3::C($c_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,) => {
($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$(,)?) => {
match $match {
$a_pattern => $crate::EitherOf4::A($a_expression),
$b_pattern => $crate::EitherOf4::B($b_expression),
@@ -797,7 +797,7 @@ macro_rules! either {
$d_pattern => $crate::EitherOf4::D($d_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,) => {
($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$(,)?) => {
match $match {
$a_pattern => $crate::EitherOf5::A($a_expression),
$b_pattern => $crate::EitherOf5::B($b_expression),
@@ -806,7 +806,7 @@ macro_rules! either {
$e_pattern => $crate::EitherOf5::E($e_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,) => {
($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$(,)?) => {
match $match {
$a_pattern => $crate::EitherOf6::A($a_expression),
$b_pattern => $crate::EitherOf6::B($b_expression),
@@ -816,7 +816,7 @@ macro_rules! either {
$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: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),
@@ -827,7 +827,7 @@ macro_rules! either {
$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: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),

View File

@@ -65,7 +65,7 @@ panic = "abort"
[package.metadata.cargo-all-features]
denylist = ["axum", "tower", "tower-http", "tokio", "sqlx", "leptos_axum"]
skip_feature_sets = [["ssr", "hydrate"]]
skip_feature_sets = [["ssr", "hydrate"], []]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name

View File

@@ -1,2 +0,0 @@
[toolchain]
channel = "stable" # test change

View File

@@ -26,8 +26,7 @@ async fn main() {
};
use axum_js_ssr::app::*;
use http_body_util::BodyExt;
use leptos::logging::log;
use leptos::prelude::*;
use leptos::{logging::log, prelude::*};
use leptos_axum::{generate_route_list, LeptosRoutes};
latency::LATENCY.get_or_init(|| [0, 4, 40, 400].iter().cycle().into());

View File

@@ -1,7 +1,3 @@
extend = [
{ path = "./lint.toml" }
]
[tasks.make-target-site-dir]
command = "mkdir"
args = ["-p", "target/site"]
@@ -24,21 +20,16 @@ clear = true
dependencies = ["check-debug", "check-release"]
[tasks.check-debug]
toolchain = "stable"
dependencies = ["cargo-all-features"]
command = "cargo"
args = ["check-all-features"]
install_crate = "cargo-all-features"
args = ["all-features", "clippy"]
[tasks.check-release]
toolchain = "stable"
dependencies = ["cargo-all-features"]
command = "cargo"
args = ["check-all-features", "--release"]
install_crate = "cargo-all-features"
[tasks.lint]
dependencies = ["make-target-site-dir", "check-style"]
args = ["all-features", "clippy", "--release"]
[tasks.start-client]
dependencies = ["install-cargo-leptos"]
command = "cargo"
args = ["leptos", "watch", "--release", "-P"]
args = ["leptos", "watch", "--release", "-P"]

View File

@@ -1,3 +1,8 @@
[tasks.cargo-all-features]
install_script = '''
cargo install --git https://github.com/sabify/cargo-all-features --branch arbitrary-command-support
'''
[tasks.install-cargo-leptos]
install_crate = { crate_name = "cargo-leptos", binary = "cargo-leptos", test_arg = "--help" }
args = ["--locked"]
@@ -16,16 +21,14 @@ clear = true
dependencies = ["check-debug", "check-release"]
[tasks.check-debug]
toolchain = "stable"
dependencies = ["cargo-all-features"]
command = "cargo"
args = ["check-all-features"]
install_crate = "cargo-all-features"
args = ["all-features", "clippy"]
[tasks.check-release]
toolchain = "stable"
dependencies = ["cargo-all-features"]
command = "cargo"
args = ["check-all-features", "--release"]
install_crate = "cargo-all-features"
args = ["all-features", "clippy", "--release"]
[tasks.start-client]
dependencies = ["install-cargo-leptos"]

View File

@@ -1,11 +1,9 @@
[tasks.build]
toolchain = "stable"
command = "cargo"
args = ["build-all-features"]
install_crate = "cargo-all-features"
[tasks.cargo-all-features]
install_script = '''
cargo install --git https://github.com/sabify/cargo-all-features --branch arbitrary-command-support
'''
[tasks.check]
toolchain = "stable"
[tasks.build]
dependencies = ["cargo-all-features"]
command = "cargo"
args = ["check-all-features"]
install_crate = "cargo-all-features"
args = ["all-features", "build"]

View File

@@ -1,3 +1,8 @@
[tasks.cargo-all-features]
install_script = '''
cargo install --git https://github.com/sabify/cargo-all-features --branch arbitrary-command-support
'''
[tasks.build]
install_crate = { crate_name = "wasm-pack", binary = "wasm-pack", test_arg = "--help" }
clear = true
@@ -14,13 +19,11 @@ clear = true
dependencies = ["check-debug", "check-release"]
[tasks.check-debug]
toolchain = "stable"
dependencies = ["cargo-all-features"]
command = "cargo"
args = ["check-all-features"]
install_crate = "cargo-all-features"
args = ["all-features", "clippy"]
[tasks.check-release]
toolchain = "stable"
dependencies = ["cargo-all-features"]
command = "cargo"
args = ["check-all-features", "--release"]
install_crate = "cargo-all-features"
args = ["all-features", "clippy", "--release"]

View File

@@ -1,8 +1,15 @@
[tasks.pre-clippy]
env = { CARGO_MAKE_CLIPPY_ARGS = "--no-deps --all-targets --all-features -- -D warnings" }
[tasks.cargo-all-features]
install_script = '''
cargo install --git https://github.com/sabify/cargo-all-features --branch arbitrary-command-support
'''
[tasks.check-style]
dependencies = ["check-format-flow", "clippy-flow"]
[tasks.lint]
dependencies = ["check-format-flow", "clippy-each-feature"]
[tasks.clippy-each-feature]
dependencies = ["cargo-all-features"]
command = "cargo"
args = ["all-features", "clippy", "--no-deps", "--", "-D", "warnings"]
[tasks.check-format]
env = { LEPTOS_PROJECT_DIRECTORY = "../../" }

View File

@@ -1,22 +1,19 @@
extend = [
{ path = "./compile.toml" },
{ path = "./clean.toml" },
{ path = "./lint.toml" },
{ path = "./node.toml" },
{ path = "./process.toml" },
{ path = "./compile.toml" },
{ path = "./clean.toml" },
{ path = "./lint.toml" },
{ path = "./node.toml" },
{ path = "./process.toml" },
]
# CI Stages
[tasks.ci]
dependencies = ["prepare", "lint", "build", "test-flow", "integration-test"]
dependencies = ["prepare", "lint", "test-flow", "integration-test"]
[tasks.prepare]
dependencies = ["setup-node"]
[tasks.lint]
dependencies = ["check-style"]
[tasks.integration-test]
# Support Local Runs

View File

@@ -1,22 +1,8 @@
extend = [
{ path = "../cargo-make/playwright.toml" },
{ path = "../cargo-make/trunk_server.toml" },
{ path = "../cargo-make/playwright.toml" },
{ path = "../cargo-make/trunk_server.toml" },
]
[tasks.integration-test]
dependencies = ["build", "start-client", "test-playwright"]
description = "Run integration test with automated start and stop of processes"
env = { SPAWN_CLIENT_PROCESS = "1" }
run_task = { name = ["start", "wait-test-stop"], parallel = true }
[tasks.wait-test-stop]
private = true
dependencies = ["wait-server", "test-playwright", "stop"]
[tasks.wait-server]
script = '''
for run in {1..12}; do
echo "Waiting to ensure server is started..."
sleep 10
done
echo "Times up, running tests"
'''

View File

@@ -6,5 +6,6 @@ command = "trunk"
args = ["build"]
[tasks.start-client]
command = "trunk"
args = ["serve", "${@}"]
script = '''
trunk serve -q "${@}" &
'''

View File

@@ -1,7 +1,3 @@
[tasks.test]
env = { RUN_CARGO_TEST = false }
condition = { env_true = ["RUN_CARGO_TEST"] }
[tasks.post-test]
dependencies = ["test-wasm"]

View File

@@ -1,8 +1,9 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<link data-trunk rel="rust" data-wasm-opt="z"/>
<link data-trunk rel="icon" type="image/ico" href="/public/favicon.ico"/>
</head>
<body></body>
</html>
<head>
<link data-trunk rel="rust" data-wasm-opt="z" />
<link data-trunk rel="icon" type="image/ico" href="/public/favicon.ico" />
</head>
<body></body>
</html>

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -1,9 +1,7 @@
#![allow(dead_code)]
use counter::*;
use leptos::mount::mount_to;
use leptos::prelude::*;
use leptos::task::tick;
use leptos::{mount::mount_to, prelude::*, task::tick};
use wasm_bindgen::JsCast;
use wasm_bindgen_test::*;

View File

@@ -44,7 +44,7 @@ ssr = [
[package.metadata.cargo-all-features]
denylist = ["actix-files", "actix-web", "leptos_actix"]
skip_feature_sets = [["ssr", "hydrate"]]
skip_feature_sets = [["ssr", "hydrate"], []]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -1,17 +1,15 @@
extend = [
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/wasm-test.toml" },
{ path = "../cargo-make/trunk_server.toml" },
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/wasm-test.toml" },
{ path = "../cargo-make/trunk_server.toml" },
]
[tasks.build]
toolchain = "stable"
dependencies = ["cargo-all-features"]
command = "cargo"
args = ["build-all-features"]
install_crate = "cargo-all-features"
args = ["all-features", "build"]
[tasks.check]
toolchain = "stable"
dependencies = ["cargo-all-features"]
command = "cargo"
args = ["check-all-features"]
install_crate = "cargo-all-features"
args = ["all-features", "clippy"]

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -5,8 +5,7 @@ use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
use counters::Counters;
use leptos::prelude::*;
use leptos::task::tick;
use leptos::{prelude::*, task::tick};
use web_sys::HtmlElement;
#[wasm_bindgen_test]
@@ -24,8 +23,9 @@ async fn inc() {
assert_eq!(
div.inner_html(),
"<button>Add Counter</button><button>Add 1000 \
Counters</button><button>Clear Counters</button><p>Total: \
<span data-testid=\"total\">0</span> from <span data-testid=\"counters\">0</span> counters.</p><ul><!----></ul>"
Counters</button><button>Clear Counters</button><p>Total: <span \
data-testid=\"total\">0</span> from <span \
data-testid=\"counters\">0</span> counters.</p><ul><!----></ul>"
);
// add 3 counters
@@ -39,8 +39,9 @@ async fn inc() {
assert_eq!(
div.inner_html(),
"<button>Add Counter</button><button>Add 1000 \
Counters</button><button>Clear Counters</button><p>Total: \
<span data-testid=\"total\">0</span> from <span data-testid=\"counters\">3</span> \
Counters</button><button>Clear Counters</button><p>Total: <span \
data-testid=\"total\">0</span> from <span \
data-testid=\"counters\">3</span> \
counters.</p><ul><li><button>-1</button><input \
type=\"text\"><span>0</span><button>+1</button><button>x</button></\
li><li><button>-1</button><input \
@@ -81,8 +82,9 @@ async fn inc() {
assert_eq!(
div.inner_html(),
"<button>Add Counter</button><button>Add 1000 \
Counters</button><button>Clear Counters</button><p>Total: \
<span data-testid=\"total\">6</span> from <span data-testid=\"counters\">3</span> \
Counters</button><button>Clear Counters</button><p>Total: <span \
data-testid=\"total\">6</span> from <span \
data-testid=\"counters\">3</span> \
counters.</p><ul><li><button>-1</button><input \
type=\"text\"><span>1</span><button>+1</button><button>x</button></\
li><li><button>-1</button><input \
@@ -106,8 +108,9 @@ async fn inc() {
assert_eq!(
div.inner_html(),
"<button>Add Counter</button><button>Add 1000 \
Counters</button><button>Clear Counters</button><p>Total: \
<span data-testid=\"total\">5</span> from <span data-testid=\"counters\">2</span> \
Counters</button><button>Clear Counters</button><p>Total: <span \
data-testid=\"total\">5</span> from <span \
data-testid=\"counters\">2</span> \
counters.</p><ul><li><button>-1</button><input \
type=\"text\"><span>2</span><button>+1</button><button>x</button></\
li><li><button>-1</button><input \

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -36,7 +36,7 @@ ssr = [
[package.metadata.cargo-all-features]
denylist = ["axum", "tower", "tower-http", "tokio", "leptos_axum"]
skip_feature_sets = [["ssr", "hydrate"]]
skip_feature_sets = [["ssr", "hydrate"], []]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -43,7 +43,7 @@ codegen-units = 1
[package.metadata.cargo-all-features]
denylist = ["actix-files", "actix-web", "leptos_actix"]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"], []]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -1,5 +1,4 @@
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub fn story(path: &str) -> String {
format!("https://node-hnapi.herokuapp.com/{path}")

View File

@@ -1,14 +1,12 @@
use crate::api;
use leptos::either::Either;
use leptos::prelude::*;
use leptos::{either::Either, prelude::*};
use leptos_meta::Meta;
use leptos_router::components::A;
use leptos_router::hooks::use_params_map;
use leptos_router::{components::A, hooks::use_params_map};
#[component]
pub fn Story() -> impl IntoView {
let params = use_params_map();
let story = Resource::new(
let story = Resource::new_blocking(
move || params.read().get("id").unwrap_or_default(),
move |id| async move {
if id.is_empty() {

View File

@@ -1,6 +1,5 @@
use crate::api::{self, User};
use leptos::server::Resource;
use leptos::{either::Either, prelude::*};
use leptos::{either::Either, prelude::*, server::Resource};
use leptos_router::hooks::use_params_map;
#[component]

View File

@@ -47,7 +47,7 @@ ssr = [
[package.metadata.cargo-all-features]
denylist = ["axum", "tower", "tower-http", "tokio", "http", "leptos_axum"]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"], []]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -1,6 +1,5 @@
use leptos::logging;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub fn story(path: &str) -> String {
format!("https://node-hnapi.herokuapp.com/{path}")

View File

@@ -1,7 +1,7 @@
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() {
use axum::Router;
use axum::{routing::get, Router};
use hackernews_axum::{shell, App};
use leptos::config::get_configuration;
use leptos_axum::{generate_route_list, LeptosRoutes};
@@ -13,6 +13,15 @@ async fn main() {
// build our application with a route
let app = Router::new()
.route(
"/favicon.ico",
get(|| async {
(
[("content-type", "image/x-icon")],
include_bytes!("../public/favicon.ico"),
)
}),
)
.leptos_routes(&leptos_options, routes, {
let leptos_options = leptos_options.clone();
move || shell(leptos_options.clone())

View File

@@ -1,14 +1,12 @@
use crate::api;
use leptos::either::Either;
use leptos::prelude::*;
use leptos::{either::Either, prelude::*};
use leptos_meta::Meta;
use leptos_router::components::A;
use leptos_router::hooks::use_params_map;
use leptos_router::{components::A, hooks::use_params_map};
#[component]
pub fn Story() -> impl IntoView {
let params = use_params_map();
let story = Resource::new(
let story = Resource::new_blocking(
move || params.read().get("id").unwrap_or_default(),
move |id| async move {
if id.is_empty() {

View File

@@ -1,6 +1,5 @@
use crate::api::{self, User};
use leptos::server::Resource;
use leptos::{either::Either, prelude::*};
use leptos::{either::Either, prelude::*, server::Resource};
use leptos_router::hooks::use_params_map;
#[component]

View File

@@ -57,7 +57,8 @@ ssr = [
[package.metadata.cargo-all-features]
denylist = ["axum", "tower", "tower-http", "tokio", "http", "leptos_axum"]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"], []]
max_combination_size = 2
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name

View File

@@ -1,8 +1,16 @@
extend = [
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/cargo-leptos-compress.toml" },
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/cargo-leptos-compress.toml" },
]
[tasks.ci]
dependencies = [
"prepare",
"make-target-site-dir",
"lint",
"test-flow",
"integration-test",
]
[env]
CLIENT_PROCESS_NAME = "hackernews_islands"

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -3,6 +3,7 @@ use axum::{
http::{header, Request, Response, StatusCode, Uri},
response::{IntoResponse, Response as AxumResponse},
};
use rust_embed::Embed;
use std::borrow::Cow;
#[cfg(not(debug_assertions))]
@@ -11,7 +12,7 @@ const DEV_MODE: bool = false;
#[cfg(debug_assertions)]
const DEV_MODE: bool = true;
#[derive(rust_embed::RustEmbed)]
#[derive(Embed)]
#[folder = "target/site/"]
struct Assets;
@@ -25,12 +26,17 @@ pub async fn file_and_error_handler(
.map(|h| h.to_str().unwrap_or("none"))
.unwrap_or("none")
.to_string();
let res = get_static_file(uri.clone(), accept_encoding).await.unwrap();
let static_result = get_static_file(uri.clone(), accept_encoding).await;
if res.status() == StatusCode::OK {
res.into_response()
} else {
(StatusCode::NOT_FOUND, "Not found.").into_response()
match static_result {
Ok(res) => {
if res.status() == StatusCode::OK {
res.into_response()
} else {
(StatusCode::NOT_FOUND, "Not found.").into_response()
}
}
Err(e) => e.into_response(),
}
}

View File

@@ -1,6 +1,7 @@
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() {
use axum::routing::get;
pub use axum::Router;
use hackernews_islands::*;
pub use leptos::config::get_configuration;
@@ -25,6 +26,7 @@ async fn main() {
// build our application with a route
let app = Router::new()
.route("/favicon.ico", get(fallback::file_and_error_handler))
.leptos_routes(&leptos_options, routes, {
let leptos_options = leptos_options.clone();
move || shell(leptos_options.clone())

View File

@@ -47,7 +47,7 @@ pub fn Stories() -> impl IntoView {
let stories = Resource::new(
move || (page(), story_type()),
move |(page, story_type)| async move {
fetch_stories(category(&story_type), page).await.ok()
fetch_stories(story_type, page).await.ok()
},
);
let (pending, set_pending) = signal(false);

View File

@@ -1,9 +1,7 @@
use crate::api;
use leptos::either::Either;
use leptos::prelude::*;
use leptos::{either::Either, prelude::*};
use leptos_meta::Meta;
use leptos_router::components::A;
use leptos_router::hooks::use_params_map;
use leptos_router::{components::A, hooks::use_params_map};
#[server]
pub async fn fetch_story(
@@ -15,7 +13,7 @@ pub async fn fetch_story(
#[component]
pub fn Story() -> impl IntoView {
let params = use_params_map();
let story = Resource::new(
let story = Resource::new_blocking(
move || params.read().get("id").unwrap_or_default(),
move |id| async move {
if id.is_empty() {

View File

@@ -1,6 +1,5 @@
use crate::api;
use leptos::server::Resource;
use leptos::{either::Either, prelude::*};
use leptos::{either::Either, prelude::*, server::Resource};
use leptos_router::hooks::use_params_map;
#[server]

View File

@@ -57,7 +57,7 @@ ssr = [
[package.metadata.cargo-all-features]
denylist = ["axum", "tower", "http", "leptos_axum"]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"], []]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -7,7 +7,7 @@ use send_wrapper::SendWrapper;
#[component]
pub fn Story() -> impl IntoView {
let params = use_params_map();
let story = Resource::new(
let story = Resource::new_blocking(
move || params.read().get("id").unwrap_or_default(),
move |id| {
SendWrapper::new(async move {

View File

@@ -41,7 +41,7 @@ panic = "abort"
[package.metadata.cargo-all-features]
denylist = ["axum", "tower", "tower-http", "tokio", "sqlx", "leptos_axum"]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"], []]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -10,7 +10,11 @@ crate-type = ["cdylib", "rlib"]
console_error_panic_hook = "0.1.7"
futures = "0.3.30"
http = "1.1"
leptos = { path = "../../leptos", features = ["tracing", "islands"] }
leptos = { path = "../../leptos", features = [
"tracing",
"islands",
"islands-router",
] }
leptos_router = { path = "../../router" }
server_fn = { path = "../../server_fn", features = ["serde-lite"] }
leptos_axum = { path = "../../integrations/axum", features = [
@@ -45,7 +49,7 @@ panic = "abort"
[package.metadata.cargo-all-features]
denylist = ["axum", "tower", "tower-http", "tokio", "sqlx", "leptos_axum"]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"], []]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -1,20 +1,19 @@
extend = [
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/wasm-test.toml" },
{ path = "../cargo-make/trunk_server.toml" },
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/wasm-test.toml" },
{ path = "../cargo-make/trunk_server.toml" },
]
[tasks.build]
toolchain = "stable"
[tasks.clippy-each-feature]
dependencies = ["cargo-all-features"]
command = "cargo"
args = ["build-all-features", "--target", "wasm32-unknown-unknown"]
install_crate = "cargo-all-features"
[tasks.check]
toolchain = "stable"
command = "cargo"
args = ["check-all-features", "--target", "wasm32-unknown-unknown"]
install_crate = "cargo-all-features"
[tasks.pre-clippy]
env = { CARGO_MAKE_CLIPPY_ARGS = "--all-targets --all-features --target wasm32-unknown-unknown -- -D warnings" }
args = [
"all-features",
"clippy",
"--target",
"wasm32-unknown-unknown",
"--no-deps",
"--",
"-D",
"warnings",
]

View File

@@ -1,2 +0,0 @@
[toolchain]
channel = "stable"

View File

@@ -169,7 +169,7 @@ pub fn App() -> impl IntoView {
let label = row.label;
let is_selected = is_selected.clone();
template! {
< tr class : danger = { move || is_selected.selected(Some(row_id)) }
< tr class : danger = { move || is_selected.selected(&Some(row_id)) }
> < td class = "col-md-1" > { row_id.to_string() } </ td > < td
class = "col-md-4" >< a on : click = move | _ | set_selected
.set(Some(row_id)) > { move || label.get() } </ a ></ td > < td

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -5,12 +5,12 @@ edition = "2021"
[dependencies]
leptos = { path = "../../leptos", features = ["csr"] }
log = "0.4.22"
log = "0.4.27"
console_log = "1.0"
console_error_panic_hook = "0.1.7"
wasm-bindgen = "0.2.93"
wasm-bindgen = "0.2.100"
[dev-dependencies]
wasm-bindgen-test = "0.3.42"
wasm-bindgen = "0.2.93"
web-sys = "0.3.70"
wasm-bindgen-test = "0.3.50"
wasm-bindgen = "0.2.100"
web-sys = "0.3.77"

View File

@@ -1,5 +1,6 @@
extend = [
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/wasm-test.toml" },
{ path = "../cargo-make/trunk_server.toml" },
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/webdriver.toml" },
{ path = "../cargo-make/wasm-test.toml" },
{ path = "../cargo-make/trunk_server.toml" },
]

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "stable" # test change
targets = ["wasm32-unknown-unknown"]

View File

@@ -1,6 +1,4 @@
use leptos::control_flow::Show;
use leptos::portal::Portal;
use leptos::prelude::*;
use leptos::{control_flow::Show, portal::Portal, prelude::*};
#[component]
pub fn App() -> impl IntoView {
@@ -10,7 +8,7 @@ pub fn App() -> impl IntoView {
view! {
<div>
<button id="btn-show" on:click=move |_| set_show_overlay.set(true)>
Show Overlay
"Show Overlay"
</button>
<Show when=move || show_overlay.get() fallback=|| ()>
@@ -19,14 +17,19 @@ pub fn App() -> impl IntoView {
<div style="position: fixed; z-index: 10; width: 100vw; height: 100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: white;">
<p>This is in the body element</p>
<button id="btn-hide" on:click=move |_| set_show_overlay.set(false)>
Close Overlay
"Close Overlay"
</button>
<button id="btn-toggle" on:click=move |_| set_show_inside_overlay.set(!show_inside_overlay.get())>
Toggle inner
<button
id="btn-toggle"
on:click=move |_| {
set_show_inside_overlay.set(!show_inside_overlay.get())
}
>
"Toggle inner"
</button>
<Show when=move || show_inside_overlay.get() fallback=|| view! { "Hidden" }>
Visible
"Visible"
</Show>
</div>
</Portal>

View File

@@ -1,13 +1,64 @@
#![allow(dead_code)]
use leptos::{leptos_dom::helpers::document, mount::mount_to, task::tick};
use portal::App;
use wasm_bindgen::JsCast;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
use leptos::task::tick;
use leptos::{leptos_dom::helpers::document, mount::mount_to};
use web_sys::HtmlButtonElement;
wasm_bindgen_test_configure!(run_in_browser);
fn minify(html: &str) -> String {
let mut result = String::with_capacity(html.len());
let mut in_tag = false;
let mut last_char_was_tag_end = false;
let mut whitespace_buffer = String::new();
for c in html.chars() {
match c {
'<' => {
// Starting a new tag
in_tag = true;
last_char_was_tag_end = false;
// Discard any buffered whitespace
whitespace_buffer.clear();
result.push(c);
}
'>' => {
// Ending a tag
in_tag = false;
last_char_was_tag_end = true;
result.push(c);
}
c if c.is_whitespace() => {
if in_tag {
// Preserve whitespace inside tags
result.push(c);
} else if !last_char_was_tag_end {
// Buffer whitespace between content
whitespace_buffer.push(c);
}
// Whitespace immediately after a tag end is ignored
}
_ => {
// Regular character
last_char_was_tag_end = false;
// If we have buffered whitespace and are outputting content,
// preserve a single space
if !whitespace_buffer.is_empty() {
result.push(' ');
whitespace_buffer.clear();
}
result.push(c);
}
}
}
result
}
#[wasm_bindgen_test]
async fn portal() {
@@ -30,7 +81,18 @@ async fn portal() {
tick().await;
// check HTML
assert_eq!(div.inner_html(), "<div><button id=\"btn-show\">Show Overlay</button><div>Show</div><!----></div><div><div style=\"position: fixed; z-index: 10; width: 100vw; height: 100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: white;\"><p>This is in the body element</p><button id=\"btn-hide\">Close Overlay</button><button id=\"btn-toggle\">Toggle inner</button>Hidden</div></div>");
assert_eq!(
minify(div.inner_html().as_str()),
minify(
"<div><button id=\"btn-show\">Show \
Overlay</button><div>Show</div><!----></div><div><div \
style=\"position: fixed; z-index: 10; width: 100vw; height: \
100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: \
white;\"><p>This is in the body element</p><button \
id=\"btn-hide\">Close Overlay</button><button \
id=\"btn-toggle\">Toggle inner</button>Hidden</div></div>"
)
);
let toggle_button = document
.get_element_by_id("btn-toggle")
@@ -39,7 +101,18 @@ async fn portal() {
toggle_button.click();
assert_eq!(div.inner_html(), "<div><button id=\"btn-show\">Show Overlay</button><div>Show</div><!----></div><div><div style=\"position: fixed; z-index: 10; width: 100vw; height: 100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: white;\"><p>This is in the body element</p><button id=\"btn-hide\">Close Overlay</button><button id=\"btn-toggle\">Toggle inner</button>Hidden</div></div>");
assert_eq!(
minify(div.inner_html().as_str()),
minify(
"<div><button id=\"btn-show\">Show \
Overlay</button><div>Show</div><!----></div><div><div \
style=\"position: fixed; z-index: 10; width: 100vw; height: \
100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: \
white;\"><p>This is in the body element</p><button \
id=\"btn-hide\">Close Overlay</button><button \
id=\"btn-toggle\">Toggle inner</button>Hidden</div></div>"
)
);
let hide_button = document
.get_element_by_id("btn-hide")
@@ -48,5 +121,40 @@ async fn portal() {
hide_button.click();
assert_eq!(div.inner_html(), "<div><button id=\"btn-show\">Show Overlay</button><div>Show</div><!----></div><div><div style=\"position: fixed; z-index: 10; width: 100vw; height: 100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: white;\"><p>This is in the body element</p><button id=\"btn-hide\">Close Overlay</button><button id=\"btn-toggle\">Toggle inner</button>Hidden</div></div>");
assert_eq!(
minify(div.inner_html().as_str()),
minify(
"<div><button id=\"btn-show\">Show \
Overlay</button><div>Show</div><!----></div><div><div \
style=\"position: fixed; z-index: 10; width: 100vw; height: \
100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: \
white;\"><p>This is in the body element</p><button \
id=\"btn-hide\">Close Overlay</button><button \
id=\"btn-toggle\">Toggle inner</button>Hidden</div></div>"
)
);
}
#[test]
fn test_minify() {
let input = r#"<div>
<p> Hello world! </p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>"#;
let expected = r#"<div><p>Hello world!</p><ul><li>Item 1</li><li>Item 2</li></ul></div>"#;
assert_eq!(minify(input), expected);
}
#[test]
fn test_preserve_whitespace_in_tags() {
let input = r#"<div class = "container">"#;
let expected = r#"<div class = "container">"#;
assert_eq!(minify(input), expected);
}

View File

@@ -1,3 +1,2 @@
[unstable]
build-std = ["std", "panic_abort", "core", "alloc"]
build-std-features = ["panic_immediate_abort"]
[target.wasm32-unknown-unknown]
rustflags = ["-C", "panic=abort"]

View File

@@ -3,6 +3,9 @@ name = "router"
version = "0.1.0"
edition = "2021"
[profile.dev]
panic = "abort"
[profile.release]
opt-level = 'z'
lto = true
@@ -22,4 +25,4 @@ tracing-subscriber-wasm = "0.1.0"
tracing = "0.1.40"
[dev-dependencies]
wasm-bindgen-test = "0.3.42"
wasm-bindgen-test = "0.3.50"

View File

@@ -1,5 +1,5 @@
extend = [
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/trunk_server.toml" },
{ path = "../cargo-make/playwright-test.toml" },
{ path = "../cargo-make/main.toml" },
{ path = "../cargo-make/trunk_server.toml" },
{ path = "../cargo-make/playwright-test.toml" },
]

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