mirror of
https://github.com/adoyle0/leptos-use.git
synced 2025-02-02 10:54:15 -05:00
use_websocket now uses codecs
This commit is contained in:
parent
a8de6a96dd
commit
4d42f7234a
20 changed files with 442 additions and 194 deletions
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -32,11 +32,11 @@ jobs:
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --features prost,serde,docs,math --tests -- -D warnings
|
run: cargo clippy --features prost,serde,docs,math --tests -- -D warnings
|
||||||
- name: Run tests (general)
|
- name: Run tests (general)
|
||||||
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde
|
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64
|
||||||
- name: Run tests (axum)
|
- name: Run tests (axum)
|
||||||
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,axum --doc use_cookie::use_cookie
|
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64,axum --doc use_cookie::use_cookie
|
||||||
- name: Run tests (actix)
|
- name: Run tests (actix)
|
||||||
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,actix --doc use_cookie::use_cookie
|
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64,actix --doc use_cookie::use_cookie
|
||||||
|
|
||||||
#### mdbook
|
#### mdbook
|
||||||
- name: Install mdbook I
|
- name: Install mdbook I
|
||||||
|
|
6
.github/workflows/tests.yml
vendored
6
.github/workflows/tests.yml
vendored
|
@ -25,8 +25,8 @@ jobs:
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --features prost,serde,docs,math --tests -- -D warnings
|
run: cargo clippy --features prost,serde,docs,math --tests -- -D warnings
|
||||||
- name: Run tests (general)
|
- name: Run tests (general)
|
||||||
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde
|
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64
|
||||||
- name: Run tests (axum)
|
- name: Run tests (axum)
|
||||||
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,axum --doc use_cookie::use_cookie
|
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64,axum --doc use_cookie::use_cookie
|
||||||
- name: Run tests (actix)
|
- name: Run tests (actix)
|
||||||
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,actix --doc use_cookie::use_cookie
|
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64,actix --doc use_cookie::use_cookie
|
||||||
|
|
1
.idea/leptos-use.iml
generated
1
.idea/leptos-use.iml
generated
|
@ -76,6 +76,7 @@
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples/use_not/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/examples/use_not/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples/use_or/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/examples/use_or/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples/use_event_source/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/examples/use_event_source/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/examples/sync_signal/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/use_event_listener/target" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/use_event_listener/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/docs/book/book" />
|
<excludeFolder url="file://$MODULE_DIR$/docs/book/book" />
|
||||||
|
|
51
CHANGELOG.md
51
CHANGELOG.md
|
@ -5,22 +5,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Changes 🔥
|
### New Features 🚀
|
||||||
|
|
||||||
|
- There are now binary codecs in addition to string codecs.
|
||||||
|
- `FromToBytesCodec`
|
||||||
|
- `WebpackSerdeCodec` (requires feature `webpack_serde`)
|
||||||
|
- `BincodeSerdeCodec` (requires feature `bincode_serde`)
|
||||||
|
- `ProstCodec` (requires feature `prost`) (see also the section "Breaking Changes 🛠" below)
|
||||||
|
- Every binary codec can be used as a string codec with the `Base64` wrapper which encodes the binary data as a base64
|
||||||
|
string.
|
||||||
|
- This required feature `base64`
|
||||||
|
- It can be wrapped for example like this: `Base64<WebpackSerdeCodec>`.
|
||||||
|
- There is now an `OptionCodec` wrapper that allows to wrap any string codec that encodes `T` to encode `Option<T>`.
|
||||||
|
- Use it like this: `OptionCodec<FromToStringCodec<f64>>`.
|
||||||
- `ElementMaybeSignal` is now implemented for `websys::HtmlElement` (thanks to @blorbb).
|
- `ElementMaybeSignal` is now implemented for `websys::HtmlElement` (thanks to @blorbb).
|
||||||
- `UseStorageOptions` now has `delay_during_hydration` which has to be used when you conditionally show parts of
|
- `UseStorageOptions` now has `delay_during_hydration` which has to be used when you conditionally show parts of
|
||||||
the DOM controlled by a value from storage. This leads to hydration errors which can be fixed by setting this new
|
the DOM controlled by a value from storage. This leads to hydration errors which can be fixed by setting this new
|
||||||
option to `true`.
|
option to `true`.
|
||||||
- `cookie::SameSite` is now re-exported
|
- `cookie::SameSite` is now re-exported
|
||||||
- Fixed typo in compiler error messages in `use_cookie` (thanks to @SleeplessOne1917).
|
- New book chapter about codecs
|
||||||
|
|
||||||
### Breaking Changes 🛠
|
### Breaking Changes 🛠
|
||||||
|
|
||||||
- `UseStorageOptions` no longer accepts a `codec` value because this is already provided as a generic parameter to
|
- `UseStorageOptions` and `UseEventSourceOptions` no longer accept a `codec` value because this is already provided as a
|
||||||
the respective function calls.
|
generic parameter to the respective function calls.
|
||||||
- `UseWebsocketOptions::reconnect_limit` is now `ReconnectLimit` instead of `u64`. Use `ReconnectLimit::Infinite` for
|
- Codecs have been refactored. There are now two traits that codecs implement: `Encoder` and `Decoder`. The
|
||||||
infinite retries or `ReconnectLimit::Limited(...)` for limited retries.
|
trait `StringCodec` is gone. The methods are now associated methods and their params now always take references.
|
||||||
- `StringCodec::decode` now takes a `&str` instead of a `String`.
|
- `JsonCodec` has been renamed to `JsonSerdeCodec`.
|
||||||
|
- The feature to enable this codec is now called `json_serde` instead of just `serde`.
|
||||||
|
- `ProstCodec` now encodes as binary data. If you want to keep using it with string data you can wrap it like
|
||||||
|
this: `Base64<ProstCodec>`. You have to enable both features `prost` and `base64` for this.
|
||||||
|
- `use_websocket`:
|
||||||
|
- `UseWebsocketOptions` has been renamed to `UseWebSocketOptions` (uppercase S) to be consistent with the return
|
||||||
|
type.
|
||||||
|
- `UseWebSocketOptions::reconnect_limit` and `UseEventSourceOptions::reconnect_limit` is now `ReconnectLimit`
|
||||||
|
instead
|
||||||
|
of `u64`. Use `ReconnectLimit::Infinite` for infinite retries or `ReconnectLimit::Limited(...)` for limited
|
||||||
|
retries.
|
||||||
|
- `use_websocket` now uses codecs to send typed messages over the network.
|
||||||
|
- When calling you have give type parameters for the message type and the
|
||||||
|
codec: `use_websocket::<String, WebpackSerdeCodec>`
|
||||||
|
- You can use binary or string codecs.
|
||||||
|
- The `UseWebSocketReturn::send` closure now takes a `&T` which is encoded using the codec.
|
||||||
|
- The `UseWebSocketReturn::message` signal now returns an `Option<T>` which is decoded using the codec.
|
||||||
|
- `UseWebSocketReturn::send_bytes` and `UseWebSocketReturn::message_bytes` are gone.
|
||||||
|
- `UseWebSocketOptions::on_message` and `UseWebSocketOptions::on_message_bytes` have been renamed
|
||||||
|
to `on_message_raw` and `on_message_raw_bytes`.
|
||||||
|
- The new `UseWebSocketOptions::on_message` takes a `&T`.
|
||||||
|
- `UseWebSocketOptions::on_error` now takes a `UseWebSocketError` instead of a `web_sys::Event`.
|
||||||
|
|
||||||
|
### Fixes 🍕
|
||||||
|
|
||||||
|
- Fixed auto-reconnect in `use_websocket`
|
||||||
|
- Fixed typo in compiler error messages in `use_cookie` (thanks to @SleeplessOne1917).
|
||||||
|
|
||||||
## [0.10.10] - 2024-05-10
|
## [0.10.10] - 2024-05-10
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
[Get Started](get_started.md)
|
[Get Started](get_started.md)
|
||||||
[Element Parameters](element_parameters.md)
|
[Element Parameters](element_parameters.md)
|
||||||
[Server-Side Rendering](server_side_rendering.md)
|
[Server-Side Rendering](server_side_rendering.md)
|
||||||
|
[Encoding and Decoding Data](codecs.md)
|
||||||
[Changelog](changelog.md)
|
[Changelog](changelog.md)
|
||||||
[Functions](functions.md)
|
[Functions](functions.md)
|
||||||
|
|
||||||
|
@ -65,6 +66,7 @@
|
||||||
|
|
||||||
- [use_event_source](network/use_event_source.md)
|
- [use_event_source](network/use_event_source.md)
|
||||||
- [use_websocket](network/use_websocket.md)
|
- [use_websocket](network/use_websocket.md)
|
||||||
|
|
||||||
<!-- - [use_webtransport](network/use_webtransport.md) -->
|
<!-- - [use_webtransport](network/use_webtransport.md) -->
|
||||||
|
|
||||||
# Animation
|
# Animation
|
||||||
|
@ -94,6 +96,7 @@
|
||||||
- [use_sorted](iterable/use_sorted.md)
|
- [use_sorted](iterable/use_sorted.md)
|
||||||
|
|
||||||
# Utilities
|
# Utilities
|
||||||
|
|
||||||
- [is_err](utilities/is_err.md)
|
- [is_err](utilities/is_err.md)
|
||||||
- [is_none](utilities/is_none.md)
|
- [is_none](utilities/is_none.md)
|
||||||
- [is_ok](utilities/is_ok.md)
|
- [is_ok](utilities/is_ok.md)
|
||||||
|
|
|
@ -8,7 +8,7 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
|
||||||
console_error_panic_hook = "0.1"
|
console_error_panic_hook = "0.1"
|
||||||
console_log = "1"
|
console_log = "1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
leptos-use = { path = "../..", features = ["docs", "prost", "serde"] }
|
leptos-use = { path = "../..", features = ["docs", "json_serde"] }
|
||||||
web-sys = "0.3"
|
web-sys = "0.3"
|
||||||
serde = "1.0.163"
|
serde = "1.0.163"
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,8 @@ leptos = { version = "0.6", features = ["nightly", "csr"] }
|
||||||
console_error_panic_hook = "0.1"
|
console_error_panic_hook = "0.1"
|
||||||
console_log = "1"
|
console_log = "1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
leptos-use = { path = "../..", features = ["docs"] }
|
leptos-use = { path = "../..", features = ["docs", "msgpack_serde"] }
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
web-sys = "0.3"
|
web-sys = "0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_use::docs::demo_or_body;
|
use leptos_use::docs::demo_or_body;
|
||||||
use leptos_use::{
|
use leptos_use::{
|
||||||
core::ConnectionReadyState, use_websocket, use_websocket_with_options, UseWebSocketOptions,
|
core::ConnectionReadyState, use_websocket, use_websocket_with_options, UseWebSocketError,
|
||||||
UseWebsocketReturn,
|
UseWebSocketOptions, UseWebSocketReturn,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use leptos_use::utils::{FromToStringCodec, MsgpackSerdeCodec};
|
||||||
use web_sys::{CloseEvent, Event};
|
use web_sys::{CloseEvent, Event};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct Apple {
|
||||||
|
name: String,
|
||||||
|
worm_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn Demo() -> impl IntoView {
|
fn Demo() -> impl IntoView {
|
||||||
let (history, set_history) = create_signal(vec![]);
|
let (history, set_history) = create_signal(vec![]);
|
||||||
|
@ -18,27 +26,22 @@ fn Demo() -> impl IntoView {
|
||||||
// use_websocket
|
// use_websocket
|
||||||
// ----------------------------
|
// ----------------------------
|
||||||
|
|
||||||
let UseWebsocketReturn {
|
let UseWebSocketReturn {
|
||||||
ready_state,
|
ready_state,
|
||||||
message,
|
message,
|
||||||
message_bytes,
|
|
||||||
send,
|
send,
|
||||||
send_bytes,
|
|
||||||
open,
|
open,
|
||||||
close,
|
close,
|
||||||
..
|
..
|
||||||
} = use_websocket("wss://echo.websocket.events/");
|
} = use_websocket::<Apple, MsgpackSerdeCodec>("wss://echo.websocket.events/");
|
||||||
|
|
||||||
let send_message = move |_| {
|
let send_message = move |_| {
|
||||||
let m = "Hello, world!";
|
let m = Apple {
|
||||||
send(m);
|
name: "More worm than apple".to_string(),
|
||||||
set_history.update(|history: &mut Vec<_>| history.push(format! {"[send]: {:?}", m}));
|
worm_count: 10,
|
||||||
};
|
};
|
||||||
|
send(&m);
|
||||||
let send_byte_message = move |_| {
|
set_history.update(|history: &mut Vec<_>| history.push(format!("[send]: {:?}", m)));
|
||||||
let m = b"Hello, world!\r\n".to_vec();
|
|
||||||
send_bytes(m.clone());
|
|
||||||
set_history.update(|history: &mut Vec<_>| history.push(format! {"[send_bytes]: {:?}", m}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let status = move || ready_state().to_string();
|
let status = move || ready_state().to_string();
|
||||||
|
@ -53,15 +56,11 @@ fn Demo() -> impl IntoView {
|
||||||
};
|
};
|
||||||
|
|
||||||
create_effect(move |_| {
|
create_effect(move |_| {
|
||||||
if let Some(m) = message.get() {
|
message.with(move |message| {
|
||||||
update_history(&set_history, format! {"[message]: {:?}", m});
|
if let Some(m) = message {
|
||||||
};
|
update_history(&set_history, format!("[message]: {:?}", m));
|
||||||
});
|
}
|
||||||
|
})
|
||||||
create_effect(move |_| {
|
|
||||||
if let Some(m) = message_bytes.get() {
|
|
||||||
update_history(&set_history, format! {"[message_bytes]: {:?}", m});
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// ----------------------------
|
// ----------------------------
|
||||||
|
@ -72,49 +71,44 @@ fn Demo() -> impl IntoView {
|
||||||
|
|
||||||
let on_open_callback = move |e: Event| {
|
let on_open_callback = move |e: Event| {
|
||||||
set_history2.update(|history: &mut Vec<_>| {
|
set_history2.update(|history: &mut Vec<_>| {
|
||||||
history.push(format! {"[onopen]: event {:?}", e.type_()})
|
history.push(format!("[onopen]: event {:?}", e.type_()))
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let on_close_callback = move |e: CloseEvent| {
|
let on_close_callback = move |e: CloseEvent| {
|
||||||
set_history2.update(|history: &mut Vec<_>| {
|
set_history2.update(|history: &mut Vec<_>| {
|
||||||
history.push(format! {"[onclose]: event {:?}", e.type_()})
|
history.push(format!("[onclose]: event {:?}", e.type_()))
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let on_error_callback = move |e: Event| {
|
let on_error_callback = move |e: UseWebSocketError<_, _>| {
|
||||||
set_history2.update(|history: &mut Vec<_>| {
|
set_history2.update(|history: &mut Vec<_>| {
|
||||||
history.push(format! {"[onerror]: event {:?}", e.type_()})
|
history.push(match e {
|
||||||
|
UseWebSocketError::Event(e) => format!("[onerror]: event {:?}", e.type_()),
|
||||||
|
_ => format!("[onerror]: {:?}", e),
|
||||||
|
})
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let on_message_callback = move |m: String| {
|
let on_message_callback = move |m: &String| {
|
||||||
set_history2.update(|history: &mut Vec<_>| history.push(format! {"[onmessage]: {:?}", m}));
|
set_history2.update(|history: &mut Vec<_>| history.push(format!("[onmessage]: {:?}", m)));
|
||||||
};
|
};
|
||||||
|
|
||||||
let on_message_bytes_callback = move |m: Vec<u8>| {
|
let UseWebSocketReturn {
|
||||||
set_history2
|
|
||||||
.update(|history: &mut Vec<_>| history.push(format! {"[onmessage_bytes]: {:?}", m}));
|
|
||||||
};
|
|
||||||
|
|
||||||
let UseWebsocketReturn {
|
|
||||||
ready_state: ready_state2,
|
ready_state: ready_state2,
|
||||||
send: send2,
|
send: send2,
|
||||||
send_bytes: send_bytes2,
|
|
||||||
open: open2,
|
open: open2,
|
||||||
close: close2,
|
close: close2,
|
||||||
message: message2,
|
message: message2,
|
||||||
message_bytes: message_bytes2,
|
|
||||||
..
|
..
|
||||||
} = use_websocket_with_options(
|
} = use_websocket_with_options::<String, FromToStringCodec>(
|
||||||
"wss://echo.websocket.events/",
|
"wss://echo.websocket.events/",
|
||||||
UseWebSocketOptions::default()
|
UseWebSocketOptions::default()
|
||||||
.immediate(false)
|
.immediate(false)
|
||||||
.on_open(on_open_callback.clone())
|
.on_open(on_open_callback.clone())
|
||||||
.on_close(on_close_callback.clone())
|
.on_close(on_close_callback.clone())
|
||||||
.on_error(on_error_callback.clone())
|
.on_error(on_error_callback.clone())
|
||||||
.on_message(on_message_callback.clone())
|
.on_message(on_message_callback.clone()),
|
||||||
.on_message_bytes(on_message_bytes_callback.clone()),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let open_connection2 = move |_| {
|
let open_connection2 = move |_| {
|
||||||
|
@ -125,28 +119,16 @@ fn Demo() -> impl IntoView {
|
||||||
};
|
};
|
||||||
|
|
||||||
let send_message2 = move |_| {
|
let send_message2 = move |_| {
|
||||||
let message = "Hello, use_leptos!";
|
let message = "Hello, use_leptos!".to_string();
|
||||||
send2(message);
|
send2(&message);
|
||||||
update_history(&set_history2, format! {"[send]: {:?}", message});
|
update_history(&set_history2, format!("[send]: {:?}", message));
|
||||||
};
|
|
||||||
|
|
||||||
let send_byte_message2 = move |_| {
|
|
||||||
let m = b"Hello, world!\r\n".to_vec();
|
|
||||||
send_bytes2(m.clone());
|
|
||||||
update_history(&set_history2, format! {"[send_bytes]: {:?}", m});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let status2 = move || ready_state2.get().to_string();
|
let status2 = move || ready_state2.get().to_string();
|
||||||
|
|
||||||
create_effect(move |_| {
|
create_effect(move |_| {
|
||||||
if let Some(m) = message2.get() {
|
if let Some(m) = message2.get() {
|
||||||
update_history(&set_history2, format! {"[message]: {:?}", m});
|
update_history(&set_history2, format!("[message]: {:?}", m));
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
create_effect(move |_| {
|
|
||||||
if let Some(m) = message_bytes2.get() {
|
|
||||||
update_history(&set_history2, format! {"[message_bytes]: {:?}", m});
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -161,9 +143,7 @@ fn Demo() -> impl IntoView {
|
||||||
<button on:click=send_message disabled=move || !connected()>
|
<button on:click=send_message disabled=move || !connected()>
|
||||||
"Send"
|
"Send"
|
||||||
</button>
|
</button>
|
||||||
<button on:click=send_byte_message disabled=move || !connected()>
|
|
||||||
"Send bytes"
|
|
||||||
</button>
|
|
||||||
<button on:click=open_connection disabled=connected>
|
<button on:click=open_connection disabled=connected>
|
||||||
"Open"
|
"Open"
|
||||||
</button>
|
</button>
|
||||||
|
@ -200,9 +180,7 @@ fn Demo() -> impl IntoView {
|
||||||
<button on:click=send_message2 disabled=move || !connected2()>
|
<button on:click=send_message2 disabled=move || !connected2()>
|
||||||
"Send"
|
"Send"
|
||||||
</button>
|
</button>
|
||||||
<button on:click=send_byte_message2 disabled=move || !connected2()>
|
|
||||||
"Send Bytes"
|
|
||||||
</button>
|
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<h3 class="text-2xl mr-2">"History"</h3>
|
<h3 class="text-2xl mr-2">"History"</h3>
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -190,7 +190,7 @@ where
|
||||||
set_data.set(default.clone());
|
set_data.set(default.clone());
|
||||||
};
|
};
|
||||||
|
|
||||||
(data.into(), set_data, remove)
|
(data, set_data, remove)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "ssr"))]
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
@ -233,7 +233,6 @@ where
|
||||||
// Fetches direct from browser storage and fills set_data if changed (memo)
|
// Fetches direct from browser storage and fills set_data if changed (memo)
|
||||||
let fetch_from_storage = {
|
let fetch_from_storage = {
|
||||||
let storage = storage.to_owned();
|
let storage = storage.to_owned();
|
||||||
let codec = codec.to_owned();
|
|
||||||
let key = key.as_ref().to_owned();
|
let key = key.as_ref().to_owned();
|
||||||
let on_error = on_error.to_owned();
|
let on_error = on_error.to_owned();
|
||||||
|
|
||||||
|
@ -248,11 +247,11 @@ where
|
||||||
handle_error(&on_error, result)
|
handle_error(&on_error, result)
|
||||||
})
|
})
|
||||||
.unwrap_or_default() // Drop handled Err(())
|
.unwrap_or_default() // Drop handled Err(())
|
||||||
|
.as_ref()
|
||||||
.map(|encoded| {
|
.map(|encoded| {
|
||||||
// Decode item
|
// Decode item
|
||||||
let result = codec
|
let result = C::decode(encoded)
|
||||||
.decode(encoded)
|
.map_err(|e| UseStorageError::ItemCodecError(CodecError::Decode(e)));
|
||||||
.map_err(UseStorageError::ItemCodecError);
|
|
||||||
handle_error(&on_error, result)
|
handle_error(&on_error, result)
|
||||||
})
|
})
|
||||||
.transpose()
|
.transpose()
|
||||||
|
@ -297,7 +296,6 @@ where
|
||||||
// Set item on internal (non-event) page changes to the data signal
|
// Set item on internal (non-event) page changes to the data signal
|
||||||
{
|
{
|
||||||
let storage = storage.to_owned();
|
let storage = storage.to_owned();
|
||||||
let codec = codec.to_owned();
|
|
||||||
let key = key.as_ref().to_owned();
|
let key = key.as_ref().to_owned();
|
||||||
let on_error = on_error.to_owned();
|
let on_error = on_error.to_owned();
|
||||||
let dispatch_storage_event = dispatch_storage_event.to_owned();
|
let dispatch_storage_event = dispatch_storage_event.to_owned();
|
||||||
|
@ -311,9 +309,8 @@ where
|
||||||
|
|
||||||
if let Ok(storage) = &storage {
|
if let Ok(storage) = &storage {
|
||||||
// Encode value
|
// Encode value
|
||||||
let result = codec
|
let result = C::encode(value)
|
||||||
.encode(value)
|
.map_err(|e| UseStorageError::ItemCodecError(CodecError::Encode(e)))
|
||||||
.map_err(UseStorageError::ItemCodecError)
|
|
||||||
.and_then(|enc_value| {
|
.and_then(|enc_value| {
|
||||||
// Set storage -- sends a global event
|
// Set storage -- sends a global event
|
||||||
storage
|
storage
|
||||||
|
@ -418,9 +415,9 @@ where
|
||||||
|
|
||||||
/// Calls the on_error callback with the given error. Removes the error from the Result to avoid double error handling.
|
/// Calls the on_error callback with the given error. Removes the error from the Result to avoid double error handling.
|
||||||
#[cfg(not(feature = "ssr"))]
|
#[cfg(not(feature = "ssr"))]
|
||||||
fn handle_error<T, Err>(
|
fn handle_error<T, E, D>(
|
||||||
on_error: &Rc<dyn Fn(UseStorageError<Err>)>,
|
on_error: &Rc<dyn Fn(UseStorageError<E, D>)>,
|
||||||
result: Result<T, UseStorageError<Err>>,
|
result: Result<T, UseStorageError<E, D>>,
|
||||||
) -> Result<T, ()> {
|
) -> Result<T, ()> {
|
||||||
result.map_err(|err| (on_error)(err))
|
result.map_err(|err| (on_error)(err))
|
||||||
}
|
}
|
||||||
|
|
|
@ -238,9 +238,11 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = cookie.with_untracked(|cookie| {
|
let value = cookie.with_untracked(|cookie| {
|
||||||
cookie
|
cookie.as_ref().and_then(|cookie| {
|
||||||
.as_ref()
|
C::encode(cookie)
|
||||||
.and_then(|cookie| C::encode(cookie).map_err(|err| on_error(err)).ok())
|
.map_err(|err| on_error(CodecError::Encode(err)))
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if value
|
if value
|
||||||
|
@ -316,7 +318,7 @@ where
|
||||||
set_cookie.set(Some(value));
|
set_cookie.set(Some(value));
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
on_error(err);
|
on_error(CodecError::Decode(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -362,7 +364,7 @@ where
|
||||||
let value = cookie
|
let value = cookie
|
||||||
.with_untracked(|cookie| {
|
.with_untracked(|cookie| {
|
||||||
cookie.as_ref().map(|cookie| {
|
cookie.as_ref().map(|cookie| {
|
||||||
C::encode(&cookie)
|
C::encode(cookie)
|
||||||
.map_err(|err| on_error(CodecError::Encode(err)))
|
.map_err(|err| on_error(CodecError::Encode(err)))
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
|
@ -858,8 +860,7 @@ fn write_server_cookie(
|
||||||
if let Some(value) = value {
|
if let Some(value) = value {
|
||||||
let cookie: Cookie = build_cookie_from_options(
|
let cookie: Cookie = build_cookie_from_options(
|
||||||
name, max_age, expires, http_only, secure, &path, same_site, &domain, &value,
|
name, max_age, expires, http_only, secure, &path, same_site, &domain, &value,
|
||||||
)
|
);
|
||||||
.into();
|
|
||||||
|
|
||||||
jar.add(cookie.into_owned());
|
jar.add(cookie.into_owned());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -30,7 +30,7 @@ use leptos::*;
|
||||||
/// On the server this function returns a Signal that is always `1.0`.
|
/// On the server this function returns a Signal that is always `1.0`.
|
||||||
pub fn use_device_pixel_ratio() -> Signal<f64> {
|
pub fn use_device_pixel_ratio() -> Signal<f64> {
|
||||||
cfg_if! { if #[cfg(feature = "ssr")] {
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
let pixel_ratio = Signal::derive(|| 1.0);
|
Signal::derive(|| 1.0)
|
||||||
} else {
|
} else {
|
||||||
use crate::{use_event_listener_with_options, UseEventListenerOptions};
|
use crate::{use_event_listener_with_options, UseEventListenerOptions};
|
||||||
use leptos::ev::change;
|
use leptos::ev::change;
|
||||||
|
@ -55,6 +55,7 @@ pub fn use_device_pixel_ratio() -> Signal<f64> {
|
||||||
.once(true),
|
.once(true),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pixel_ratio.into()
|
||||||
}}
|
}}
|
||||||
pixel_ratio.into()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,19 +246,13 @@ where
|
||||||
es.set_onerror(Some(on_error.as_ref().unchecked_ref()));
|
es.set_onerror(Some(on_error.as_ref().unchecked_ref()));
|
||||||
on_error.forget();
|
on_error.forget();
|
||||||
|
|
||||||
let on_message = Closure::wrap(Box::new({
|
let on_message = Closure::wrap(Box::new(move |e: web_sys::MessageEvent| {
|
||||||
let set_data_from_string = set_data_from_string.clone();
|
set_data_from_string(e.data().as_string());
|
||||||
|
|
||||||
move |e: web_sys::MessageEvent| {
|
|
||||||
set_data_from_string(e.data().as_string());
|
|
||||||
}
|
|
||||||
}) as Box<dyn FnMut(web_sys::MessageEvent)>);
|
}) as Box<dyn FnMut(web_sys::MessageEvent)>);
|
||||||
es.set_onmessage(Some(on_message.as_ref().unchecked_ref()));
|
es.set_onmessage(Some(on_message.as_ref().unchecked_ref()));
|
||||||
on_message.forget();
|
on_message.forget();
|
||||||
|
|
||||||
for event_name in named_events.clone() {
|
for event_name in named_events.clone() {
|
||||||
let set_data_from_string = set_data_from_string.clone();
|
|
||||||
|
|
||||||
let _ = use_event_listener(
|
let _ = use_event_listener(
|
||||||
es.clone(),
|
es.clone(),
|
||||||
ev::Custom::<ev::Event>::new(event_name),
|
ev::Custom::<ev::Event>::new(event_name),
|
||||||
|
|
|
@ -5,8 +5,12 @@ use leptos::{leptos_dom::helpers::TimeoutHandle, *};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::core::ConnectionReadyState;
|
use crate::core::ConnectionReadyState;
|
||||||
|
use crate::utils::{
|
||||||
|
CodecError, Decoder, Encoder, HybridCoderError, HybridDecoder, HybridEncoder, IsBinary,
|
||||||
|
};
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use js_sys::Array;
|
use js_sys::Array;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
@ -20,30 +24,30 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
|
||||||
///
|
///
|
||||||
/// ## Usage
|
/// ## Usage
|
||||||
///
|
///
|
||||||
|
/// Values are (en)decoded via the given codec. You can use any of the codecs, string or binary.
|
||||||
|
///
|
||||||
|
/// > Please check [the codec chapter](https://leptos-use.rs/codecs.html) to see what codecs are
|
||||||
|
/// available and what feature flags they require.
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos::*;
|
/// # use leptos::*;
|
||||||
/// # use leptos_use::{use_websocket, UseWebsocketReturn};
|
/// # use leptos_use::utils::FromToStringCodec;
|
||||||
|
/// # use leptos_use::{use_websocket, UseWebSocketReturn};
|
||||||
/// # use leptos_use::core::ConnectionReadyState;
|
/// # use leptos_use::core::ConnectionReadyState;
|
||||||
/// #
|
/// #
|
||||||
/// # #[component]
|
/// # #[component]
|
||||||
/// # fn Demo() -> impl IntoView {
|
/// # fn Demo() -> impl IntoView {
|
||||||
/// let UseWebsocketReturn {
|
/// let UseWebSocketReturn {
|
||||||
/// ready_state,
|
/// ready_state,
|
||||||
/// message,
|
/// message,
|
||||||
/// message_bytes,
|
|
||||||
/// send,
|
/// send,
|
||||||
/// send_bytes,
|
|
||||||
/// open,
|
/// open,
|
||||||
/// close,
|
/// close,
|
||||||
/// ..
|
/// ..
|
||||||
/// } = use_websocket("wss://echo.websocket.events/");
|
/// } = use_websocket::<String, FromToStringCodec>("wss://echo.websocket.events/");
|
||||||
///
|
///
|
||||||
/// let send_message = move |_| {
|
/// let send_message = move |_| {
|
||||||
/// send("Hello, world!");
|
/// send(&"Hello, world!".to_string());
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// let send_byte_message = move |_| {
|
|
||||||
/// send_bytes(b"Hello, world!\r\n".to_vec());
|
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// let status = move || ready_state.get().to_string();
|
/// let status = move || ready_state.get().to_string();
|
||||||
|
@ -63,17 +67,49 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
|
||||||
/// <p>"status: " {status}</p>
|
/// <p>"status: " {status}</p>
|
||||||
///
|
///
|
||||||
/// <button on:click=send_message disabled=move || !connected()>"Send"</button>
|
/// <button on:click=send_message disabled=move || !connected()>"Send"</button>
|
||||||
/// <button on:click=send_byte_message disabled=move || !connected()>"Send bytes"</button>
|
|
||||||
/// <button on:click=open_connection disabled=connected>"Open"</button>
|
/// <button on:click=open_connection disabled=connected>"Open"</button>
|
||||||
/// <button on:click=close_connection disabled=move || !connected()>"Close"</button>
|
/// <button on:click=close_connection disabled=move || !connected()>"Close"</button>
|
||||||
///
|
///
|
||||||
/// <p>"Receive message: " {move || format!("{:?}", message.get())}</p>
|
/// <p>"Receive message: " {move || format!("{:?}", message.get())}</p>
|
||||||
/// <p>"Receive byte message: " {move || format!("{:?}", message_bytes.get())}</p>
|
|
||||||
/// </div>
|
/// </div>
|
||||||
/// }
|
/// }
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// Here is another example using `msgpack` for encoding and decoding. This means that only binary
|
||||||
|
/// messages can be sent or received. For this to work you have to enable the **`msgpack_serde` feature** flag.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use leptos::*;
|
||||||
|
/// # use leptos_use::utils::MsgpackSerdeCodec;
|
||||||
|
/// # use leptos_use::{use_websocket, UseWebSocketReturn};
|
||||||
|
/// # use serde::{Deserialize, Serialize};
|
||||||
|
/// #
|
||||||
|
/// # #[component]
|
||||||
|
/// # fn Demo() -> impl IntoView {
|
||||||
|
/// #[derive(Serialize, Deserialize)]
|
||||||
|
/// struct SomeData {
|
||||||
|
/// name: String,
|
||||||
|
/// count: i32,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let UseWebSocketReturn {
|
||||||
|
/// message,
|
||||||
|
/// send,
|
||||||
|
/// ..
|
||||||
|
/// } = use_websocket::<SomeData, MsgpackSerdeCodec>("wss://some.websocket.server/");
|
||||||
|
///
|
||||||
|
/// let send_data = move || {
|
||||||
|
/// send(&SomeData {
|
||||||
|
/// name: "John Doe".to_string(),
|
||||||
|
/// count: 42,
|
||||||
|
/// });
|
||||||
|
/// };
|
||||||
|
/// #
|
||||||
|
/// # view! {}
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
/// ## Relative Paths
|
/// ## Relative Paths
|
||||||
///
|
///
|
||||||
/// If the provided `url` is relative, it will be resolved relative to the current page.
|
/// If the provided `url` is relative, it will be resolved relative to the current page.
|
||||||
|
@ -105,11 +141,11 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
|
||||||
/// #[derive(Clone)]
|
/// #[derive(Clone)]
|
||||||
/// pub struct WebsocketContext {
|
/// pub struct WebsocketContext {
|
||||||
/// pub message: Signal<Option<String>>,
|
/// pub message: Signal<Option<String>>,
|
||||||
/// send: Rc<dyn Fn(&str)>, // use Rc to make it easily cloneable
|
/// send: Rc<dyn Fn(&String)>, // use Rc to make it easily cloneable
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl WebsocketContext {
|
/// impl WebsocketContext {
|
||||||
/// pub fn new(message: Signal<Option<String>>, send: Rc<dyn Fn(&str)>) -> Self {
|
/// pub fn new(message: Signal<Option<String>>, send: Rc<dyn Fn(&String)>) -> Self {
|
||||||
/// Self {
|
/// Self {
|
||||||
/// message,
|
/// message,
|
||||||
/// send,
|
/// send,
|
||||||
|
@ -119,7 +155,7 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
|
||||||
/// // create a method to avoid having to use parantheses around the field
|
/// // create a method to avoid having to use parantheses around the field
|
||||||
/// #[inline(always)]
|
/// #[inline(always)]
|
||||||
/// pub fn send(&self, message: &str) {
|
/// pub fn send(&self, message: &str) {
|
||||||
/// (self.send)(message)
|
/// (self.send)(&message.to_string())
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -128,16 +164,17 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos::*;
|
/// # use leptos::*;
|
||||||
/// # use leptos_use::{use_websocket, UseWebsocketReturn};
|
/// # use leptos_use::utils::FromToStringCodec;
|
||||||
|
/// # use leptos_use::{use_websocket, UseWebSocketReturn};
|
||||||
/// # use std::rc::Rc;
|
/// # use std::rc::Rc;
|
||||||
/// # #[derive(Clone)]
|
/// # #[derive(Clone)]
|
||||||
/// # pub struct WebsocketContext {
|
/// # pub struct WebsocketContext {
|
||||||
/// # pub message: Signal<Option<String>>,
|
/// # pub message: Signal<Option<String>>,
|
||||||
/// # send: Rc<dyn Fn(&str)>,
|
/// # send: Rc<dyn Fn(&String)>,
|
||||||
/// # }
|
/// # }
|
||||||
/// #
|
/// #
|
||||||
/// # impl WebsocketContext {
|
/// # impl WebsocketContext {
|
||||||
/// # pub fn new(message: Signal<Option<String>>, send: Rc<dyn Fn(&str)>) -> Self {
|
/// # pub fn new(message: Signal<Option<String>>, send: Rc<dyn Fn(&String)>) -> Self {
|
||||||
/// # Self {
|
/// # Self {
|
||||||
/// # message,
|
/// # message,
|
||||||
/// # send,
|
/// # send,
|
||||||
|
@ -147,11 +184,11 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
|
||||||
///
|
///
|
||||||
/// # #[component]
|
/// # #[component]
|
||||||
/// # fn Demo() -> impl IntoView {
|
/// # fn Demo() -> impl IntoView {
|
||||||
/// let UseWebsocketReturn {
|
/// let UseWebSocketReturn {
|
||||||
/// message,
|
/// message,
|
||||||
/// send,
|
/// send,
|
||||||
/// ..
|
/// ..
|
||||||
/// } = use_websocket("ws:://some.websocket.io");
|
/// } = use_websocket::<String, FromToStringCodec>("ws:://some.websocket.io");
|
||||||
///
|
///
|
||||||
/// provide_context(WebsocketContext::new(message, Rc::new(send.clone())));
|
/// provide_context(WebsocketContext::new(message, Rc::new(send.clone())));
|
||||||
/// #
|
/// #
|
||||||
|
@ -163,18 +200,18 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use leptos::*;
|
/// # use leptos::*;
|
||||||
/// # use leptos_use::{use_websocket, UseWebsocketReturn};
|
/// # use leptos_use::{use_websocket, UseWebSocketReturn};
|
||||||
/// # use std::rc::Rc;
|
/// # use std::rc::Rc;
|
||||||
/// # #[derive(Clone)]
|
/// # #[derive(Clone)]
|
||||||
/// # pub struct WebsocketContext {
|
/// # pub struct WebsocketContext {
|
||||||
/// # pub message: Signal<Option<String>>,
|
/// # pub message: Signal<Option<String>>,
|
||||||
/// # send: Rc<dyn Fn(&str)>,
|
/// # send: Rc<dyn Fn(&String)>,
|
||||||
/// # }
|
/// # }
|
||||||
/// #
|
/// #
|
||||||
/// # impl WebsocketContext {
|
/// # impl WebsocketContext {
|
||||||
/// # #[inline(always)]
|
/// # #[inline(always)]
|
||||||
/// # pub fn send(&self, message: &str) {
|
/// # pub fn send(&self, message: &str) {
|
||||||
/// # (self.send)(message)
|
/// # (self.send)(&message.to_string())
|
||||||
/// # }
|
/// # }
|
||||||
/// # }
|
/// # }
|
||||||
///
|
///
|
||||||
|
@ -191,32 +228,52 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
|
||||||
/// ## Server-Side Rendering
|
/// ## Server-Side Rendering
|
||||||
///
|
///
|
||||||
/// On the server the returned functions amount to no-ops.
|
/// On the server the returned functions amount to no-ops.
|
||||||
pub fn use_websocket(
|
pub fn use_websocket<T, C>(
|
||||||
url: &str,
|
url: &str,
|
||||||
) -> UseWebsocketReturn<
|
) -> UseWebSocketReturn<
|
||||||
|
T,
|
||||||
impl Fn() + Clone + 'static,
|
impl Fn() + Clone + 'static,
|
||||||
impl Fn() + Clone + 'static,
|
impl Fn() + Clone + 'static,
|
||||||
impl Fn(&str) + Clone + 'static,
|
impl Fn(&T) + Clone + 'static,
|
||||||
impl Fn(Vec<u8>) + Clone + 'static,
|
>
|
||||||
> {
|
where
|
||||||
use_websocket_with_options(url, UseWebSocketOptions::default())
|
T: 'static,
|
||||||
|
C: Encoder<T> + Decoder<T>,
|
||||||
|
C: IsBinary<T, <C as Decoder<T>>::Encoded>,
|
||||||
|
C: HybridDecoder<T, <C as Decoder<T>>::Encoded, Error = <C as Decoder<T>>::Error>,
|
||||||
|
C: HybridEncoder<T, <C as Encoder<T>>::Encoded, Error = <C as Encoder<T>>::Error>,
|
||||||
|
{
|
||||||
|
use_websocket_with_options::<T, C>(url, UseWebSocketOptions::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version of [`use_websocket`] that takes `UseWebSocketOptions`. See [`use_websocket`] for how to use.
|
/// Version of [`use_websocket`] that takes `UseWebSocketOptions`. See [`use_websocket`] for how to use.
|
||||||
pub fn use_websocket_with_options(
|
pub fn use_websocket_with_options<T, C>(
|
||||||
url: &str,
|
url: &str,
|
||||||
options: UseWebSocketOptions,
|
options: UseWebSocketOptions<
|
||||||
) -> UseWebsocketReturn<
|
T,
|
||||||
|
HybridCoderError<<C as Encoder<T>>::Error>,
|
||||||
|
HybridCoderError<<C as Decoder<T>>::Error>,
|
||||||
|
>,
|
||||||
|
) -> UseWebSocketReturn<
|
||||||
|
T,
|
||||||
impl Fn() + Clone + 'static,
|
impl Fn() + Clone + 'static,
|
||||||
impl Fn() + Clone + 'static,
|
impl Fn() + Clone + 'static,
|
||||||
impl Fn(&str) + Clone + 'static,
|
impl Fn(&T) + Clone + 'static,
|
||||||
impl Fn(Vec<u8>) + Clone,
|
>
|
||||||
> {
|
where
|
||||||
|
T: 'static,
|
||||||
|
C: Encoder<T> + Decoder<T>,
|
||||||
|
C: IsBinary<T, <C as Decoder<T>>::Encoded>,
|
||||||
|
C: HybridDecoder<T, <C as Decoder<T>>::Encoded, Error = <C as Decoder<T>>::Error>,
|
||||||
|
C: HybridEncoder<T, <C as Encoder<T>>::Encoded, Error = <C as Encoder<T>>::Error>,
|
||||||
|
{
|
||||||
let url = normalize_url(url);
|
let url = normalize_url(url);
|
||||||
|
|
||||||
let UseWebSocketOptions {
|
let UseWebSocketOptions {
|
||||||
on_open,
|
on_open,
|
||||||
on_message,
|
on_message,
|
||||||
on_message_bytes,
|
on_message_raw,
|
||||||
|
on_message_raw_bytes,
|
||||||
on_error,
|
on_error,
|
||||||
on_close,
|
on_close,
|
||||||
reconnect_limit,
|
reconnect_limit,
|
||||||
|
@ -227,7 +284,6 @@ pub fn use_websocket_with_options(
|
||||||
|
|
||||||
let (ready_state, set_ready_state) = create_signal(ConnectionReadyState::Closed);
|
let (ready_state, set_ready_state) = create_signal(ConnectionReadyState::Closed);
|
||||||
let (message, set_message) = create_signal(None);
|
let (message, set_message) = create_signal(None);
|
||||||
let (message_bytes, set_message_bytes) = create_signal(None);
|
|
||||||
let ws_ref: StoredValue<Option<WebSocket>> = store_value(None);
|
let ws_ref: StoredValue<Option<WebSocket>> = store_value(None);
|
||||||
|
|
||||||
let reconnect_timer_ref: StoredValue<Option<TimeoutHandle>> = store_value(None);
|
let reconnect_timer_ref: StoredValue<Option<TimeoutHandle>> = store_value(None);
|
||||||
|
@ -268,6 +324,7 @@ pub fn use_websocket_with_options(
|
||||||
|
|
||||||
connect_ref.set_value({
|
connect_ref.set_value({
|
||||||
let unmounted = Rc::clone(&unmounted);
|
let unmounted = Rc::clone(&unmounted);
|
||||||
|
let on_error = Rc::clone(&on_error);
|
||||||
|
|
||||||
Some(Rc::new(move || {
|
Some(Rc::new(move || {
|
||||||
reconnect_timer_ref.set_value(None);
|
reconnect_timer_ref.set_value(None);
|
||||||
|
@ -322,7 +379,9 @@ pub fn use_websocket_with_options(
|
||||||
{
|
{
|
||||||
let unmounted = Rc::clone(&unmounted);
|
let unmounted = Rc::clone(&unmounted);
|
||||||
let on_message = Rc::clone(&on_message);
|
let on_message = Rc::clone(&on_message);
|
||||||
let on_message_bytes = Rc::clone(&on_message_bytes);
|
let on_message_raw = Rc::clone(&on_message_raw);
|
||||||
|
let on_message_raw_bytes = Rc::clone(&on_message_raw_bytes);
|
||||||
|
let on_error = Rc::clone(&on_error);
|
||||||
|
|
||||||
let onmessage_closure = Closure::wrap(Box::new(move |e: MessageEvent| {
|
let onmessage_closure = Closure::wrap(Box::new(move |e: MessageEvent| {
|
||||||
if unmounted.get() {
|
if unmounted.get() {
|
||||||
|
@ -344,12 +403,27 @@ pub fn use_websocket_with_options(
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
let prev = SpecialNonReactiveZone::enter();
|
let prev = SpecialNonReactiveZone::enter();
|
||||||
|
|
||||||
on_message(txt.clone());
|
on_message_raw(&txt);
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
SpecialNonReactiveZone::exit(prev);
|
SpecialNonReactiveZone::exit(prev);
|
||||||
|
|
||||||
set_message.set(Some(txt));
|
match C::decode_str(&txt) {
|
||||||
|
Ok(val) => {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
let prev = SpecialNonReactiveZone::enter();
|
||||||
|
|
||||||
|
on_message(&val);
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
SpecialNonReactiveZone::exit(prev);
|
||||||
|
|
||||||
|
set_message.set(Some(val));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
on_error(CodecError::Decode(err).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -360,12 +434,27 @@ pub fn use_websocket_with_options(
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
let prev = SpecialNonReactiveZone::enter();
|
let prev = SpecialNonReactiveZone::enter();
|
||||||
|
|
||||||
on_message_bytes(array.clone());
|
on_message_raw_bytes(&array);
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
SpecialNonReactiveZone::exit(prev);
|
SpecialNonReactiveZone::exit(prev);
|
||||||
|
|
||||||
set_message_bytes.set(Some(array));
|
match C::decode_bin(array.as_slice()) {
|
||||||
|
Ok(val) => {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
let prev = SpecialNonReactiveZone::enter();
|
||||||
|
|
||||||
|
on_message(&val);
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
SpecialNonReactiveZone::exit(prev);
|
||||||
|
|
||||||
|
set_message.set(Some(val));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
on_error(CodecError::Decode(err).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -391,7 +480,7 @@ pub fn use_websocket_with_options(
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
let prev = SpecialNonReactiveZone::enter();
|
let prev = SpecialNonReactiveZone::enter();
|
||||||
|
|
||||||
on_error(e);
|
on_error(UseWebSocketError::Event(e));
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
SpecialNonReactiveZone::exit(prev);
|
SpecialNonReactiveZone::exit(prev);
|
||||||
|
@ -438,7 +527,7 @@ pub fn use_websocket_with_options(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send text (String)
|
// Send text (String)
|
||||||
let send = {
|
let send_str = {
|
||||||
Box::new(move |data: &str| {
|
Box::new(move |data: &str| {
|
||||||
if ready_state.get_untracked() == ConnectionReadyState::Open {
|
if ready_state.get_untracked() == ConnectionReadyState::Open {
|
||||||
if let Some(web_socket) = ws_ref.get_value() {
|
if let Some(web_socket) = ws_ref.get_value() {
|
||||||
|
@ -449,10 +538,28 @@ pub fn use_websocket_with_options(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send bytes
|
// Send bytes
|
||||||
let send_bytes = move |data: Vec<u8>| {
|
let send_bytes = move |data: &[u8]| {
|
||||||
if ready_state.get_untracked() == ConnectionReadyState::Open {
|
if ready_state.get_untracked() == ConnectionReadyState::Open {
|
||||||
if let Some(web_socket) = ws_ref.get_value() {
|
if let Some(web_socket) = ws_ref.get_value() {
|
||||||
let _ = web_socket.send_with_u8_array(&data);
|
let _ = web_socket.send_with_u8_array(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let send = {
|
||||||
|
let on_error = Rc::clone(&on_error);
|
||||||
|
|
||||||
|
move |value: &T| {
|
||||||
|
if C::is_binary() {
|
||||||
|
match C::encode_bin(value) {
|
||||||
|
Ok(val) => send_bytes(&val),
|
||||||
|
Err(err) => on_error(CodecError::Encode(err).into()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match C::encode_str(value) {
|
||||||
|
Ok(val) => send_str(&val),
|
||||||
|
Err(err) => on_error(CodecError::Encode(err).into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -490,15 +597,13 @@ pub fn use_websocket_with_options(
|
||||||
close();
|
close();
|
||||||
});
|
});
|
||||||
|
|
||||||
UseWebsocketReturn {
|
UseWebSocketReturn {
|
||||||
ready_state: ready_state.into(),
|
ready_state: ready_state.into(),
|
||||||
message: message.into(),
|
message: message.into(),
|
||||||
message_bytes: message_bytes.into(),
|
|
||||||
ws: ws_ref.get_value(),
|
ws: ws_ref.get_value(),
|
||||||
open,
|
open,
|
||||||
close,
|
close,
|
||||||
send,
|
send,
|
||||||
send_bytes,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
@ -522,17 +627,26 @@ impl ReconnectLimit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RcFnBytes = Rc<dyn Fn(&[u8])>;
|
||||||
|
|
||||||
/// Options for [`use_websocket_with_options`].
|
/// Options for [`use_websocket_with_options`].
|
||||||
#[derive(DefaultBuilder)]
|
#[derive(DefaultBuilder)]
|
||||||
pub struct UseWebSocketOptions {
|
pub struct UseWebSocketOptions<T, E, D>
|
||||||
|
where
|
||||||
|
T: ?Sized,
|
||||||
|
{
|
||||||
/// `WebSocket` connect callback.
|
/// `WebSocket` connect callback.
|
||||||
on_open: Rc<dyn Fn(Event)>,
|
on_open: Rc<dyn Fn(Event)>,
|
||||||
|
/// `WebSocket` message callback for typed message decoded by codec.
|
||||||
|
#[builder(skip)]
|
||||||
|
on_message: Rc<dyn Fn(&T)>,
|
||||||
/// `WebSocket` message callback for text.
|
/// `WebSocket` message callback for text.
|
||||||
on_message: Rc<dyn Fn(String)>,
|
on_message_raw: Rc<dyn Fn(&str)>,
|
||||||
/// `WebSocket` message callback for binary.
|
/// `WebSocket` message callback for binary.
|
||||||
on_message_bytes: Rc<dyn Fn(Vec<u8>)>,
|
on_message_raw_bytes: RcFnBytes,
|
||||||
/// `WebSocket` error callback.
|
/// `WebSocket` error callback.
|
||||||
on_error: Rc<dyn Fn(Event)>,
|
#[builder(skip)]
|
||||||
|
on_error: Rc<dyn Fn(UseWebSocketError<E, D>)>,
|
||||||
/// `WebSocket` close callback.
|
/// `WebSocket` close callback.
|
||||||
on_close: Rc<dyn Fn(CloseEvent)>,
|
on_close: Rc<dyn Fn(CloseEvent)>,
|
||||||
/// Retry times. Defaults to `ReconnectLimit::Limited(3)`. Use `ReconnectLimit::Infinite` for
|
/// Retry times. Defaults to `ReconnectLimit::Limited(3)`. Use `ReconnectLimit::Infinite` for
|
||||||
|
@ -548,12 +662,37 @@ pub struct UseWebSocketOptions {
|
||||||
protocols: Option<Vec<String>>,
|
protocols: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UseWebSocketOptions {
|
impl<T: ?Sized, E, D> UseWebSocketOptions<T, E, D> {
|
||||||
|
/// `WebSocket` error callback.
|
||||||
|
pub fn on_error<F>(self, handler: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(UseWebSocketError<E, D>) + 'static,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
on_error: Rc::new(handler),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `WebSocket` message callback for typed message decoded by codec.
|
||||||
|
pub fn on_message<F>(self, handler: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(&T) + 'static,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
on_message: Rc::new(handler),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized, E, D> Default for UseWebSocketOptions<T, E, D> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
on_open: Rc::new(|_| {}),
|
on_open: Rc::new(|_| {}),
|
||||||
on_message: Rc::new(|_| {}),
|
on_message: Rc::new(|_| {}),
|
||||||
on_message_bytes: Rc::new(|_| {}),
|
on_message_raw: Rc::new(|_| {}),
|
||||||
|
on_message_raw_bytes: Rc::new(|_| {}),
|
||||||
on_error: Rc::new(|_| {}),
|
on_error: Rc::new(|_| {}),
|
||||||
on_close: Rc::new(|_| {}),
|
on_close: Rc::new(|_| {}),
|
||||||
reconnect_limit: ReconnectLimit::default(),
|
reconnect_limit: ReconnectLimit::default(),
|
||||||
|
@ -566,29 +705,33 @@ impl Default for UseWebSocketOptions {
|
||||||
|
|
||||||
/// Return type of [`use_websocket`].
|
/// Return type of [`use_websocket`].
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct UseWebsocketReturn<OpenFn, CloseFn, SendFn, SendBytesFn>
|
pub struct UseWebSocketReturn<T, OpenFn, CloseFn, SendFn>
|
||||||
where
|
where
|
||||||
|
T: 'static,
|
||||||
OpenFn: Fn() + Clone + 'static,
|
OpenFn: Fn() + Clone + 'static,
|
||||||
CloseFn: Fn() + Clone + 'static,
|
CloseFn: Fn() + Clone + 'static,
|
||||||
SendFn: Fn(&str) + Clone + 'static,
|
SendFn: Fn(&T) + Clone + 'static,
|
||||||
SendBytesFn: Fn(Vec<u8>) + Clone + 'static,
|
|
||||||
{
|
{
|
||||||
/// The current state of the `WebSocket` connection.
|
/// The current state of the `WebSocket` connection.
|
||||||
pub ready_state: Signal<ConnectionReadyState>,
|
pub ready_state: Signal<ConnectionReadyState>,
|
||||||
/// Latest text message received from `WebSocket`.
|
/// Latest message received from `WebSocket`.
|
||||||
pub message: Signal<Option<String>>,
|
pub message: Signal<Option<T>>,
|
||||||
/// Latest binary message received from `WebSocket`.
|
|
||||||
pub message_bytes: Signal<Option<Vec<u8>>>,
|
|
||||||
/// The `WebSocket` instance.
|
/// The `WebSocket` instance.
|
||||||
pub ws: Option<WebSocket>,
|
pub ws: Option<WebSocket>,
|
||||||
/// Opens the `WebSocket` connection
|
/// Opens the `WebSocket` connection
|
||||||
pub open: OpenFn,
|
pub open: OpenFn,
|
||||||
/// Closes the `WebSocket` connection
|
/// Closes the `WebSocket` connection
|
||||||
pub close: CloseFn,
|
pub close: CloseFn,
|
||||||
/// Sends `text` (string) based data
|
/// Sends data through the socket
|
||||||
pub send: SendFn,
|
pub send: SendFn,
|
||||||
/// Sends binary data
|
}
|
||||||
pub send_bytes: SendBytesFn,
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum UseWebSocketError<E, D> {
|
||||||
|
#[error("WebSocket error event")]
|
||||||
|
Event(Event),
|
||||||
|
#[error("WebSocket codec error: {0}")]
|
||||||
|
Codec(#[from] CodecError<E, D>),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalize_url(url: &str) -> String {
|
fn normalize_url(url: &str) -> String {
|
||||||
|
|
|
@ -21,7 +21,7 @@ use web_sys::WebTransportBidirectionalStream;
|
||||||
#[cfg(feature = "bincode")]
|
#[cfg(feature = "bincode")]
|
||||||
use bincode::serde::{decode_from_slice as from_slice, encode_to_vec as to_vec};
|
use bincode::serde::{decode_from_slice as from_slice, encode_to_vec as to_vec};
|
||||||
|
|
||||||
///
|
/// This still under development and will not arrive before Leptos 0.7.
|
||||||
///
|
///
|
||||||
/// ## Demo
|
/// ## Demo
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::utils::{Decoder, Encoder};
|
use crate::utils::{Decoder, Encoder};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// A codec that relies on `bincode` adn `serde` to encode data in the bincode format.
|
/// A codec that relies on `bincode` adn `serde` to encode data in the bincode format.
|
||||||
///
|
///
|
||||||
|
|
|
@ -6,11 +6,11 @@ mod msgpack_serde;
|
||||||
#[cfg(feature = "prost")]
|
#[cfg(feature = "prost")]
|
||||||
mod prost;
|
mod prost;
|
||||||
|
|
||||||
#[cfg(feature = "bincode")]
|
#[cfg(feature = "bincode_serde")]
|
||||||
pub use bincode_serde::*;
|
pub use bincode_serde::*;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use from_to_bytes::*;
|
pub use from_to_bytes::*;
|
||||||
#[cfg(feature = "msgpack")]
|
#[cfg(feature = "msgpack_serde")]
|
||||||
pub use msgpack_serde::*;
|
pub use msgpack_serde::*;
|
||||||
#[cfg(feature = "prost")]
|
#[cfg(feature = "prost")]
|
||||||
pub use prost::*;
|
pub use prost::*;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::utils::{Decoder, Encoder};
|
use crate::utils::{Decoder, Encoder};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// A codec that relies on `rmp-serde` to encode data in the msgpack format.
|
/// A codec that relies on `rmp-serde` to encode data in the msgpack format.
|
||||||
///
|
///
|
||||||
|
|
108
src/utils/codecs/hybrid.rs
Normal file
108
src/utils/codecs/hybrid.rs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
use crate::utils::{Decoder, Encoder};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub trait IsBinary<T, E: ?Sized> {
|
||||||
|
fn is_binary() -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D, T> IsBinary<T, [u8]> for D
|
||||||
|
where
|
||||||
|
D: Decoder<T, Encoded = [u8]>,
|
||||||
|
{
|
||||||
|
fn is_binary() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D, T> IsBinary<T, str> for D
|
||||||
|
where
|
||||||
|
D: Decoder<T, Encoded = str>,
|
||||||
|
{
|
||||||
|
fn is_binary() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum HybridCoderError<E> {
|
||||||
|
#[error("Not implemented: {0}")]
|
||||||
|
NotImplemented(&'static str),
|
||||||
|
#[error("Decoding error")]
|
||||||
|
Coder(#[from] E),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait HybridDecoder<T, E: ?Sized> {
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
fn decode_str(_val: &str) -> Result<T, HybridCoderError<Self::Error>> {
|
||||||
|
Err(HybridCoderError::NotImplemented(
|
||||||
|
"You're trying to decode from a string. This codec is binary.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_bin(_val: &[u8]) -> Result<T, HybridCoderError<Self::Error>> {
|
||||||
|
Err(HybridCoderError::NotImplemented(
|
||||||
|
"You're trying to decode from a byte slice. This codec is a string codec.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, D> HybridDecoder<T, [u8]> for D
|
||||||
|
where
|
||||||
|
D: Decoder<T, Encoded = [u8]>,
|
||||||
|
{
|
||||||
|
type Error = D::Error;
|
||||||
|
|
||||||
|
fn decode_bin(val: &[u8]) -> Result<T, HybridCoderError<Self::Error>> {
|
||||||
|
Ok(D::decode(val)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, D> HybridDecoder<T, str> for D
|
||||||
|
where
|
||||||
|
D: Decoder<T, Encoded = str>,
|
||||||
|
{
|
||||||
|
type Error = D::Error;
|
||||||
|
|
||||||
|
fn decode_str(val: &str) -> Result<T, HybridCoderError<Self::Error>> {
|
||||||
|
Ok(D::decode(val)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait HybridEncoder<T, E> {
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
fn encode_str(_val: &T) -> Result<String, HybridCoderError<Self::Error>> {
|
||||||
|
Err(HybridCoderError::NotImplemented(
|
||||||
|
"You're trying to encode into a string. This codec is binary.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_bin(_val: &T) -> Result<Vec<u8>, HybridCoderError<Self::Error>> {
|
||||||
|
Err(HybridCoderError::NotImplemented(
|
||||||
|
"You're trying to encode into a byte vec. This codec is a string codec.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> HybridEncoder<T, Vec<u8>> for E
|
||||||
|
where
|
||||||
|
E: Encoder<T, Encoded = Vec<u8>>,
|
||||||
|
{
|
||||||
|
type Error = E::Error;
|
||||||
|
|
||||||
|
fn encode_bin(val: &T) -> Result<Vec<u8>, HybridCoderError<Self::Error>> {
|
||||||
|
Ok(E::encode(val)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> HybridEncoder<T, String> for E
|
||||||
|
where
|
||||||
|
E: Encoder<T, Encoded = String>,
|
||||||
|
{
|
||||||
|
type Error = E::Error;
|
||||||
|
|
||||||
|
fn encode_str(val: &T) -> Result<String, HybridCoderError<Self::Error>> {
|
||||||
|
Ok(E::encode(val)?)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
mod bin;
|
mod bin;
|
||||||
|
mod hybrid;
|
||||||
mod string;
|
mod string;
|
||||||
|
|
||||||
pub use bin::*;
|
pub use bin::*;
|
||||||
|
pub use hybrid::*;
|
||||||
pub use string::*;
|
pub use string::*;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -21,22 +23,6 @@ pub trait Decoder<T>: 'static {
|
||||||
fn decode(val: &Self::Encoded) -> Result<T, Self::Error>;
|
fn decode(val: &Self::Encoded) -> Result<T, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait to check if a type is binary or encodes data in a string.
|
|
||||||
pub trait IsBinary<T> {
|
|
||||||
fn is_binary() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Enc, T> IsBinary<T> for Enc
|
|
||||||
where
|
|
||||||
Enc: Encoder<T, Encoded = String>,
|
|
||||||
{
|
|
||||||
fn is_binary() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum CodecError<E, D> {
|
pub enum CodecError<E, D> {
|
||||||
#[error("failed to encode: {0}")]
|
#[error("failed to encode: {0}")]
|
||||||
|
|
|
@ -62,6 +62,6 @@ where
|
||||||
|
|
||||||
fn decode(val: &Self::Encoded) -> Result<T, Self::Error> {
|
fn decode(val: &Self::Encoded) -> Result<T, Self::Error> {
|
||||||
let buf = base64::engine::general_purpose::STANDARD.decode(val)?;
|
let buf = base64::engine::general_purpose::STANDARD.decode(val)?;
|
||||||
D::decode(&buf).map_err(|err| Base64DecodeError::Decoder(err))
|
D::decode(&buf).map_err(Base64DecodeError::Decoder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue