From cd0d494ed91c00e9c386d1b7cceec8f022e6be7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtte?= Date: Sat, 21 Feb 2026 18:45:32 +0100 Subject: [PATCH 1/8] Make ChannelTask public, but hidden in docu --- src/channel_task.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/channel_task.rs b/src/channel_task.rs index 1205f5b..23f26f2 100644 --- a/src/channel_task.rs +++ b/src/channel_task.rs @@ -34,7 +34,8 @@ pub struct ChannelTask { impl ChannelTask { /// Create a new `ChannelTask` from a channel and a result receiver. - pub(crate) fn new(channel: Channel, result_rx: oneshot::Receiver>) -> Self { + #[doc(hidden)] + pub fn new(channel: Channel, result_rx: oneshot::Receiver>) -> Self { Self { channel, result_rx, From 5b1c2965b77942b9e48eda6ca8bf4de54ae823a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtte?= Date: Sun, 1 Mar 2026 20:04:40 +0100 Subject: [PATCH 2/8] Added pot as optional dependency --- Cargo.toml | 7 +++++-- src/convert.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aa6bf70..5f312e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,6 @@ keywords.workspace = true [dependencies] futures = "0.3" js-sys = { version = "0.3" } -postcard = { version = "1.1", features = ["alloc"] } send_wrapper = "0.6" serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11" @@ -40,6 +39,8 @@ tokio = { version = "1.4", features = ["sync"] } wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" log = "0.4" +postcard = { version = "1.1", features = ["alloc"], optional = true } +pot = { version = "3.0.1", optional = true } [dependencies.web-sys] features = [ @@ -63,8 +64,10 @@ version = "0.3" wasmworker-proc-macro = { workspace = true } [features] -default = ["serde"] +default = ["serde", "codec-postcard"] serde = [] +codec-postcard = ["dep:postcard"] +codec-pot = ["dep:pot"] macros = ["wasmworker-proc-macro"] [dependencies.wasmworker-proc-macro] diff --git a/src/convert.rs b/src/convert.rs index 0b1be58..ffd3559 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; /// This wrapper function encapsulates our internal serialization format. /// It is used internally to prepare values before sending them to a worker /// or back to the main thread via `postMessage`. +#[cfg(feature = "codec-postcard")] pub fn to_bytes(value: &T) -> Box<[u8]> { postcard::to_allocvec(value) .expect("WebWorker serialization failed") @@ -12,6 +13,31 @@ pub fn to_bytes(value: &T) -> Box<[u8]> { /// This wrapper function encapsulates our internal serialization format. /// It is used internally to prepare values after receiving them from a worker /// or the main thread via `postMessage`. +#[cfg(feature = "codec-postcard")] pub fn from_bytes<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> T { postcard::from_bytes(bytes).expect("WebWorker deserialization failed") } + +#[cfg(feature = "codec-pot")] +pub const POT_CONFIG: pot::Config = pot::Config::new().compatibility(pot::Compatibility::V4); + +/// This wrapper function encapsulates our internal serialization format. +/// It is used internally to prepare values before sending them to a worker +/// or back to the main thread via `postMessage`. +#[cfg(feature = "codec-pot")] +pub fn to_bytes(value: &T) -> Box<[u8]> { + POT_CONFIG + .serialize(self) + .expect("WebWorker serialization failed") + .into() +} + +/// This wrapper function encapsulates our internal serialization format. +/// It is used internally to prepare values after receiving them from a worker +/// or the main thread via `postMessage`. +#[cfg(feature = "codec-pot")] +pub fn from_bytes<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> T { + POT_CONFIG + .deserialize(bytes) + expect("WebWorker deserialization failed") +} From 0e6280078498c8fd1a3695d97a63f38f58041c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtte?= Date: Sun, 1 Mar 2026 20:08:58 +0100 Subject: [PATCH 3/8] Fixed build errors --- Cargo.lock | 29 +++++++++++++++++++++++++++++ src/convert.rs | 4 ++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1516d62..bd08f42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "embedded-io" version = "0.4.0" @@ -158,6 +164,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + [[package]] name = "hash32" version = "0.2.1" @@ -249,6 +266,17 @@ dependencies = [ "serde", ] +[[package]] +name = "pot" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf741fa415952eb20f27fbc210dc85f31cc7cdc80aa3ce81d5e27d28a6f45dc2" +dependencies = [ + "byteorder", + "half", + "serde", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -530,6 +558,7 @@ dependencies = [ "js-sys", "log", "postcard", + "pot", "send_wrapper", "serde", "serde-wasm-bindgen", diff --git a/src/convert.rs b/src/convert.rs index ffd3559..7bac66b 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -27,7 +27,7 @@ pub const POT_CONFIG: pot::Config = pot::Config::new().compatibility(pot::Compat #[cfg(feature = "codec-pot")] pub fn to_bytes(value: &T) -> Box<[u8]> { POT_CONFIG - .serialize(self) + .serialize(value) .expect("WebWorker serialization failed") .into() } @@ -39,5 +39,5 @@ pub fn to_bytes(value: &T) -> Box<[u8]> { pub fn from_bytes<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> T { POT_CONFIG .deserialize(bytes) - expect("WebWorker deserialization failed") + .expect("WebWorker deserialization failed") } From 0d9260cac32ced858c64907e474d2cff5b743938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtte?= Date: Sun, 1 Mar 2026 20:16:04 +0100 Subject: [PATCH 4/8] Fixed clippy all features build --- .github/workflows/test.yml | 3 ++- src/convert.rs | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ade05d5..ee16f2d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,8 @@ jobs: with: components: clippy - uses: Swatinem/rust-cache@v2 - - run: cargo clippy --all-features -- -D warnings + - run: cargo clippy --no-default-features --features "serde,codec-postcard" -- -D warnings + - run: cargo clippy --no-default-features --features "serde,codec-pot" -- -D warnings doc-test: runs-on: ubuntu-latest diff --git a/src/convert.rs b/src/convert.rs index 7bac66b..ab829f9 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -41,3 +41,10 @@ pub fn from_bytes<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> T { .deserialize(bytes) .expect("WebWorker deserialization failed") } + +// Enforce exactly one: +#[cfg(all(feature = "codec-postcard", feature = "codec-pot"))] +compile_error!("Enable only one of: codec-postcard, codec-pot"); + +#[cfg(not(any(feature = "codec-postcard", feature = "codec-pot")))] +compile_error!("Enable one of: codec-postcard, codec-pot"); From 395fee8209de39723350c3cf81efb91f3633e558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtte?= Date: Sun, 8 Mar 2026 19:39:46 +0100 Subject: [PATCH 5/8] Make pot config not public Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/convert.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/convert.rs b/src/convert.rs index ab829f9..2c5352e 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -19,7 +19,7 @@ pub fn from_bytes<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> T { } #[cfg(feature = "codec-pot")] -pub const POT_CONFIG: pot::Config = pot::Config::new().compatibility(pot::Compatibility::V4); +const POT_CONFIG: pot::Config = pot::Config::new().compatibility(pot::Compatibility::V4); /// This wrapper function encapsulates our internal serialization format. /// It is used internally to prepare values before sending them to a worker From 760d50799e7eb79b4754f8b72952935b7c9ae919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtte?= Date: Sun, 8 Mar 2026 19:48:43 +0100 Subject: [PATCH 6/8] Make pot only optional feature --- .github/workflows/test.yml | 3 +-- Cargo.toml | 5 ++--- src/convert.rs | 11 ++--------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ee16f2d..ade05d5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,8 +22,7 @@ jobs: with: components: clippy - uses: Swatinem/rust-cache@v2 - - run: cargo clippy --no-default-features --features "serde,codec-postcard" -- -D warnings - - run: cargo clippy --no-default-features --features "serde,codec-pot" -- -D warnings + - run: cargo clippy --all-features -- -D warnings doc-test: runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 5f312e4..9623830 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ tokio = { version = "1.4", features = ["sync"] } wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" log = "0.4" -postcard = { version = "1.1", features = ["alloc"], optional = true } +postcard = { version = "1.1", features = ["alloc"] } pot = { version = "3.0.1", optional = true } [dependencies.web-sys] @@ -64,9 +64,8 @@ version = "0.3" wasmworker-proc-macro = { workspace = true } [features] -default = ["serde", "codec-postcard"] +default = ["serde"] serde = [] -codec-postcard = ["dep:postcard"] codec-pot = ["dep:pot"] macros = ["wasmworker-proc-macro"] diff --git a/src/convert.rs b/src/convert.rs index 2c5352e..f0e0736 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; /// This wrapper function encapsulates our internal serialization format. /// It is used internally to prepare values before sending them to a worker /// or back to the main thread via `postMessage`. -#[cfg(feature = "codec-postcard")] +#[cfg(not(feature = "codec-pot"))] pub fn to_bytes(value: &T) -> Box<[u8]> { postcard::to_allocvec(value) .expect("WebWorker serialization failed") @@ -13,7 +13,7 @@ pub fn to_bytes(value: &T) -> Box<[u8]> { /// This wrapper function encapsulates our internal serialization format. /// It is used internally to prepare values after receiving them from a worker /// or the main thread via `postMessage`. -#[cfg(feature = "codec-postcard")] +#[cfg(not(feature = "codec-pot"))] pub fn from_bytes<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> T { postcard::from_bytes(bytes).expect("WebWorker deserialization failed") } @@ -41,10 +41,3 @@ pub fn from_bytes<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> T { .deserialize(bytes) .expect("WebWorker deserialization failed") } - -// Enforce exactly one: -#[cfg(all(feature = "codec-postcard", feature = "codec-pot"))] -compile_error!("Enable only one of: codec-postcard, codec-pot"); - -#[cfg(not(any(feature = "codec-postcard", feature = "codec-pot")))] -compile_error!("Enable one of: codec-postcard, codec-pot"); From e2a76389b711be767c024928796c5f53e8849dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtte?= Date: Sun, 8 Mar 2026 21:34:46 +0100 Subject: [PATCH 7/8] Better feature split for codecs --- Cargo.toml | 5 +++-- src/convert.rs | 13 ++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9623830..5f312e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ tokio = { version = "1.4", features = ["sync"] } wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" log = "0.4" -postcard = { version = "1.1", features = ["alloc"] } +postcard = { version = "1.1", features = ["alloc"], optional = true } pot = { version = "3.0.1", optional = true } [dependencies.web-sys] @@ -64,8 +64,9 @@ version = "0.3" wasmworker-proc-macro = { workspace = true } [features] -default = ["serde"] +default = ["serde", "codec-postcard"] serde = [] +codec-postcard = ["dep:postcard"] codec-pot = ["dep:pot"] macros = ["wasmworker-proc-macro"] diff --git a/src/convert.rs b/src/convert.rs index f0e0736..d525a64 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; /// This wrapper function encapsulates our internal serialization format. /// It is used internally to prepare values before sending them to a worker /// or back to the main thread via `postMessage`. -#[cfg(not(feature = "codec-pot"))] +#[cfg(feature = "codec-postcard")] pub fn to_bytes(value: &T) -> Box<[u8]> { postcard::to_allocvec(value) .expect("WebWorker serialization failed") @@ -13,18 +13,18 @@ pub fn to_bytes(value: &T) -> Box<[u8]> { /// This wrapper function encapsulates our internal serialization format. /// It is used internally to prepare values after receiving them from a worker /// or the main thread via `postMessage`. -#[cfg(not(feature = "codec-pot"))] +#[cfg(feature = "codec-postcard")] pub fn from_bytes<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> T { postcard::from_bytes(bytes).expect("WebWorker deserialization failed") } -#[cfg(feature = "codec-pot")] +#[cfg(all(feature = "codec-pot", not(feature = "codec-postcard")))] const POT_CONFIG: pot::Config = pot::Config::new().compatibility(pot::Compatibility::V4); /// This wrapper function encapsulates our internal serialization format. /// It is used internally to prepare values before sending them to a worker /// or back to the main thread via `postMessage`. -#[cfg(feature = "codec-pot")] +#[cfg(all(feature = "codec-pot", not(feature = "codec-postcard")))] pub fn to_bytes(value: &T) -> Box<[u8]> { POT_CONFIG .serialize(value) @@ -35,9 +35,12 @@ pub fn to_bytes(value: &T) -> Box<[u8]> { /// This wrapper function encapsulates our internal serialization format. /// It is used internally to prepare values after receiving them from a worker /// or the main thread via `postMessage`. -#[cfg(feature = "codec-pot")] +#[cfg(all(feature = "codec-pot", not(feature = "codec-postcard")))] pub fn from_bytes<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> T { POT_CONFIG .deserialize(bytes) .expect("WebWorker deserialization failed") } + +#[cfg(not(any(feature = "codec-postcard", feature = "codec-pot")))] +compile_error!("No codec selected. Enable `codec-postcard` (default) or `codec-pot`."); From 62c046be90f5769ae89bd718668e70a8d2fbc0d4 Mon Sep 17 00:00:00 2001 From: Pascal Berrang Date: Sun, 8 Mar 2026 21:59:07 +0000 Subject: [PATCH 8/8] Add codec documentation to README and extend CI for pot feature --- .github/workflows/test.yml | 10 ++++++++-- README.md | 13 +++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ade05d5..ff2af0b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,6 +15,9 @@ jobs: clippy: runs-on: ubuntu-latest + strategy: + matrix: + features: ["--all-features", "--no-default-features --features serde,codec-pot"] steps: - uses: actions/checkout@v4 @@ -22,17 +25,20 @@ jobs: with: components: clippy - uses: Swatinem/rust-cache@v2 - - run: cargo clippy --all-features -- -D warnings + - run: cargo clippy ${{ matrix.features }} -- -D warnings doc-test: runs-on: ubuntu-latest + strategy: + matrix: + features: ["--all-features", "--no-default-features --features serde,macros,codec-pot"] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: Run doctest - run: cargo test --doc --all-features + run: cargo test --doc ${{ matrix.features }} test: runs-on: ubuntu-latest diff --git a/README.md b/README.md index 2387f6f..e83fc98 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ In contrast to many other libraries like [wasm-bindgen-rayon](https://github.com - [Usage](#usage) - [Setting up](#setting-up) + - [Serialization codec](#serialization-codec) - [Outsourcing tasks](#outsourcing-tasks) - [WebWorker](#webworker) - [WebWorkerPool](#webworkerpool) @@ -38,6 +39,18 @@ The `wasmworker` crate comes with a default feature called `serde`, which allows Without the `serde` feature, only functions with the type `fn(Box<[u8]>) -> Box<[u8]>` can be run on a worker. This is useful for users that do not want a direct serde dependency. Internally, the library always uses serde, though. +#### Serialization codec +By default, `wasmworker` uses [postcard](https://crates.io/crates/postcard) for internal serialization. +Postcard is compact and fast, making it ideal for the typical WebWorker use case (passing `Vec`, structs, primitives). + +For complex types like `Rc` or cyclic structures, you can use [pot](https://crates.io/crates/pot) instead. +Note that pot has significantly higher serialization overhead and larger output sizes, so it should only be used when postcard cannot handle your data types. + +```toml +[dependencies] +wasmworker = { version = "0.3", default-features = false, features = ["serde", "macros", "codec-pot"] } +``` + You can then start using the library without further setup. If you plan on using the global `WebWorkerPool` (using the iterator extensions or `worker_pool()`), you can *optionally* configure this pool: ```rust