codecs extracted into crate codee

This commit is contained in:
Maccesch 2024-07-08 17:10:29 +01:00
parent c20d78c2ea
commit e4ad9f11af
30 changed files with 70 additions and 725 deletions

2
.github/FUNDING.yml vendored
View file

@ -1,6 +1,6 @@
# These are supported funding model platforms
github: [Synphonyte]# Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
github: [Synphonyte] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username

View file

@ -30,13 +30,13 @@ jobs:
- name: Check formatting
run: cargo fmt --check
- name: Clippy
run: cargo clippy --features prost,serde,docs,math --tests -- -D warnings
run: cargo clippy --features docs,math --tests -- -D warnings
- name: Run tests (general)
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64
run: cargo test --features math,docs,ssr
- name: Run tests (axum)
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64,axum --doc use_cookie::use_cookie
run: cargo test --features math,docs,ssr,axum --doc use_cookie::use_cookie
- name: Run tests (actix)
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64,actix --doc use_cookie::use_cookie
run: cargo test --features math,docs,ssr,actix --doc use_cookie::use_cookie
#### mdbook
- name: Install mdbook I

View file

@ -23,10 +23,10 @@ jobs:
- name: Check formatting
run: cargo fmt --check
- name: Clippy
run: cargo clippy --features prost,serde,docs,math --tests -- -D warnings
run: cargo clippy --features docs,math --tests -- -D warnings
- name: Run tests (general)
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64
run: cargo test --features math,docs,ssr
- name: Run tests (axum)
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64,axum --doc use_cookie::use_cookie
run: cargo test --features math,docs,ssr,axum --doc use_cookie::use_cookie
- name: Run tests (actix)
run: cargo test --features math,docs,ssr,prost,json_serde,msgpack_serde,bincode_serde,base64,actix --doc use_cookie::use_cookie
run: cargo test --features math,docs,ssr,actix --doc use_cookie::use_cookie

View file

@ -15,9 +15,8 @@ homepage = "https://leptos-use.rs"
[dependencies]
actix-web = { version = "4", optional = true, default-features = false }
async-trait = "0.1"
base64 = { version = "0.21", optional = true }
cfg-if = "1"
bincode = { version = "1", optional = true }
codee = "0.1"
cookie = { version = "0.18", features = ["percent-encode"] }
default-struct-builder = "0.5"
futures-util = "0.3"
@ -33,10 +32,6 @@ leptos_actix = { version = "0.6", optional = true }
leptos-spin = { version = "0.1", optional = true }
num = { version = "0.4", optional = true }
paste = "1"
prost = { version = "0.12", optional = true }
rmp-serde = { version = "1.1", optional = true }
serde = { version = "1", optional = true }
serde_json = { version = "1", optional = true }
thiserror = "1"
wasm-bindgen = "0.2.92"
wasm-bindgen-futures = "0.4"
@ -136,19 +131,19 @@ features = [
getrandom = { version = "0.2", features = ["js"] }
leptos_meta = "0.6"
rand = "0.8"
codee = { version = "0.1", features = ["json_serde", "msgpack_serde", "base64", "prost"] }
serde = { version = "1", features = ["derive"] }
[features]
actix = ["dep:actix-web", "dep:leptos_actix", "dep:http0_2"]
axum = ["dep:leptos_axum", "dep:http1"]
docs = []
math = ["num"]
prost = ["dep:prost"]
json_serde = ["dep:serde_json", "dep:serde"]
spin = ["dep:leptos-spin", "dep:http1"]
ssr = []
msgpack_serde = ["dep:rmp-serde", "dep:serde"]
bincode_serde = ["dep:bincode", "dep:serde"]
wasm_ssr = []
[package.metadata.docs.rs]
features = ["math", "docs", "ssr", "prost", "json_serde", "msgpack_serde", "bincode_serde"]
features = ["math", "docs", "ssr"]
rustdoc-args = ["--cfg=web_sys_unstable_apis"]
rustc-args = ["--cfg=web_sys_unstable_apis"]

View file

@ -2,6 +2,7 @@
[Introduction](introduction.md)
[Get Started](get_started.md)
[Options](options.md)
[Element Parameters](element_parameters.md)
[Server-Side Rendering](server_side_rendering.md)
[Encoding and Decoding Data](codecs.md)

22
docs/book/src/options.md Normal file
View file

@ -0,0 +1,22 @@
# Options
Most functions in Leptos-Use come with a version `..._with_options`. For example `use_css_var` has a
version `use_css_var_with_options`. As the name suggests, you can provide additional options to those versions of the
functions.
These options are defined as structs with the corresponding PascalCase name. For our example `use_css_var_with_options`
the name of the struct is `UseCssVarOptions`. Every option struct implements `Default` and the builder pattern to
make it easy to change only the values needed. This can look like the following example.
```rust
let (color, set_color) = use_css_var_with_options(
"--color",
UseCssVarOptions::default()
.target(el)
.initial_value("#eee"),
);
```
Here only the values `target` and `initial_value` are changed and everything else is left to default.
TODO : automatic conversion like Fn and Option

View file

@ -4,11 +4,12 @@ version = "0.1.0"
edition = "2021"
[dependencies]
codee = { path = "../../../codee", features = ["json_serde"] }
leptos = { version = "0.6", features = ["nightly", "csr"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
leptos-use = { path = "../..", features = ["docs", "json_serde"] }
leptos-use = { path = "../..", features = ["docs"] }
web-sys = "0.3"
serde = "1.0.163"

View file

@ -5,10 +5,11 @@ edition = "2021"
[dependencies]
leptos = { version = "0.6", features = ["nightly", "csr"] }
codee = { path = "../../../codee", features = ["msgpack_serde"] }
console_error_panic_hook = "0.1"
console_log = "1"
log = "0.4"
leptos-use = { path = "../..", features = ["docs", "msgpack_serde"] }
leptos-use = { path = "../..", features = ["docs"] }
serde = { version = "1", features = ["derive"] }
web-sys = "0.3"

View file

@ -6,7 +6,7 @@ use leptos_use::{
};
use serde::{Deserialize, Serialize};
use leptos_use::utils::{FromToStringCodec, MsgpackSerdeCodec};
use codee::{binary::MsgpackSerdeCodec, string::FromToStringCodec};
use web_sys::{CloseEvent, Event};
#[derive(Serialize, Deserialize, Debug)]

View file

@ -1,5 +1,5 @@
use super::{use_storage_with_options, StorageType, UseStorageOptions};
use crate::utils::{Decoder, Encoder};
use codee::{Decoder, Encoder};
use leptos::signal_prelude::*;
/// Reactive [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).

View file

@ -1,5 +1,5 @@
use super::{use_storage_with_options, StorageType, UseStorageOptions};
use crate::utils::{Decoder, Encoder};
use codee::{Decoder, Encoder};
use leptos::signal_prelude::*;
/// Reactive [SessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage).

View file

@ -1,8 +1,8 @@
use crate::utils::{CodecError, Decoder, Encoder};
use crate::{
core::{MaybeRwSignal, StorageType},
utils::FilterOptions,
};
use codee::{CodecError, Decoder, Encoder};
use default_struct_builder::DefaultBuilder;
use leptos::*;
use std::rc::Rc;
@ -39,7 +39,8 @@ const INTERNAL_STORAGE_EVENT: &str = "leptos-use-storage";
/// # use leptos::*;
/// # use leptos_use::storage::{StorageType, use_local_storage, use_session_storage, use_storage};
/// # use serde::{Deserialize, Serialize};
/// # use leptos_use::utils::{FromToStringCodec, JsonSerdeCodec, ProstCodec, Base64};
/// # use codee::string::{FromToStringCodec, JsonSerdeCodec, Base64};
/// # use codee::binary::ProstCodec;
/// #
/// # #[component]
/// # pub fn Demo() -> impl IntoView {
@ -94,7 +95,7 @@ const INTERNAL_STORAGE_EVENT: &str = "leptos-use-storage";
/// ```
/// # use leptos::*;
/// # use leptos_use::storage::use_session_storage;
/// # use leptos_use::utils::FromToStringCodec;
/// # use codee::string::FromToStringCodec;
/// #
/// # #[component]
/// # pub fn Example() -> impl IntoView {
@ -127,7 +128,7 @@ const INTERNAL_STORAGE_EVENT: &str = "leptos-use-storage";
/// ```
/// # use leptos::*;
/// # use leptos_use::storage::{use_local_storage_with_options, UseStorageOptions};
/// # use leptos_use::utils::FromToStringCodec;
/// # use codee::string::FromToStringCodec;
/// #
/// # #[component]
/// # pub fn Example() -> impl IntoView {

View file

@ -1,7 +1,7 @@
use crate::utils::{CodecError, Decoder, Encoder};
use crate::{
js, use_event_listener, use_event_listener_with_options, use_supported, UseEventListenerOptions,
};
use codee::{CodecError, Decoder, Encoder};
use leptos::*;
use thiserror::Error;
use wasm_bindgen::JsValue;
@ -23,7 +23,7 @@ use wasm_bindgen::JsValue;
/// ```
/// # use leptos::*;
/// # use leptos_use::{use_broadcast_channel, UseBroadcastChannelReturn};
/// # use leptos_use::utils::FromToStringCodec;
/// # use codee::string::FromToStringCodec;
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {
@ -54,7 +54,7 @@ use wasm_bindgen::JsValue;
/// # use leptos::*;
/// # use serde::{Deserialize, Serialize};
/// # use leptos_use::use_broadcast_channel;
/// # use leptos_use::utils::JsonSerdeCodec;
/// # use codee::string::JsonSerdeCodec;
/// #
/// // Data sent in JSON must implement Serialize, Deserialize:
/// #[derive(Serialize, Deserialize, Clone, PartialEq)]

View file

@ -2,8 +2,8 @@ use crate::core::url;
use crate::core::StorageType;
use crate::core::{ElementMaybeSignal, MaybeRwSignal};
use crate::storage::{use_storage_with_options, UseStorageOptions};
use crate::utils::FromToStringCodec;
use crate::{sync_signal_with_options, use_cookie, use_preferred_dark, SyncSignalOptions};
use codee::string::FromToStringCodec;
use default_struct_builder::DefaultBuilder;
use leptos::*;
use std::fmt::{Display, Formatter};

View file

@ -1,7 +1,7 @@
#![allow(clippy::too_many_arguments)]
use crate::core::now;
use crate::utils::{CodecError, Decoder, Encoder};
use codee::{CodecError, Decoder, Encoder};
use cookie::time::{Duration, OffsetDateTime};
pub use cookie::SameSite;
use cookie::{Cookie, CookieJar};
@ -30,7 +30,7 @@ use std::rc::Rc;
/// ```
/// # use leptos::*;
/// # use leptos_use::use_cookie;
/// # use leptos_use::utils::FromToStringCodec;
/// # use codee::string::FromToStringCodec;
/// # use rand::prelude::*;
///
/// #
@ -70,7 +70,7 @@ use std::rc::Rc;
/// # use cookie::SameSite;
/// # use leptos::*;
/// # use leptos_use::{use_cookie_with_options, UseCookieOptions};
/// # use leptos_use::utils::FromToStringCodec;
/// # use codee::string::FromToStringCodec;
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {
@ -105,7 +105,7 @@ use std::rc::Rc;
/// # use leptos::*;
/// # use serde::{Deserialize, Serialize};
/// # use leptos_use::{use_cookie_with_options, UseCookieOptions};
/// # use leptos_use::utils::JsonSerdeCodec;
/// # use codee::string::JsonSerdeCodec;
/// #
/// # #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
/// # pub struct Auth {
@ -215,10 +215,10 @@ where
#[cfg(not(feature = "ssr"))]
{
use crate::utils::{FromToStringCodec, OptionCodec};
use crate::{
use_broadcast_channel, watch_pausable, UseBroadcastChannelReturn, WatchPausableReturn,
};
use codee::string::{FromToStringCodec, OptionCodec};
let UseBroadcastChannelReturn { message, post, .. } =
use_broadcast_channel::<Option<String>, OptionCodec<FromToStringCodec>>(&format!(

View file

@ -1,6 +1,6 @@
use crate::core::ConnectionReadyState;
use crate::utils::Decoder;
use crate::{js, use_event_listener, ReconnectLimit};
use codee::Decoder;
use default_struct_builder::DefaultBuilder;
use leptos::*;
use std::cell::Cell;
@ -27,7 +27,8 @@ use thiserror::Error;
///
/// ```
/// # use leptos::*;
/// # use leptos_use::{use_event_source, UseEventSourceReturn, utils::JsonSerdeCodec};
/// # use leptos_use::{use_event_source, UseEventSourceReturn};
/// # use codee::string::JsonSerdeCodec;
/// # use serde::{Deserialize, Serialize};
/// #
/// #[derive(Serialize, Deserialize, Clone, PartialEq)]
@ -56,7 +57,8 @@ use thiserror::Error;
///
/// ```
/// # use leptos::*;
/// # use leptos_use::{use_event_source_with_options, UseEventSourceReturn, UseEventSourceOptions, utils::FromToStringCodec};
/// # use leptos_use::{use_event_source_with_options, UseEventSourceReturn, UseEventSourceOptions};
/// # use codee::string::FromToStringCodec;
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {
@ -87,7 +89,8 @@ use thiserror::Error;
///
/// ```
/// # use leptos::*;
/// # use leptos_use::{use_event_source_with_options, UseEventSourceReturn, UseEventSourceOptions, utils::FromToStringCodec, ReconnectLimit};
/// # use leptos_use::{use_event_source_with_options, UseEventSourceReturn, UseEventSourceOptions, ReconnectLimit};
/// # use codee::string::FromToStringCodec;
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {

View file

@ -8,7 +8,7 @@ use std::time::Duration;
use thiserror::Error;
use crate::core::ConnectionReadyState;
use crate::utils::{
use codee::{
CodecError, Decoder, Encoder, HybridCoderError, HybridDecoder, HybridEncoder, IsBinary,
};
use default_struct_builder::DefaultBuilder;
@ -31,7 +31,7 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
///
/// ```
/// # use leptos::*;
/// # use leptos_use::utils::FromToStringCodec;
/// # use codee::string::FromToStringCodec;
/// # use leptos_use::{use_websocket, UseWebSocketReturn};
/// # use leptos_use::core::ConnectionReadyState;
/// #
@ -81,7 +81,7 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
///
/// ```
/// # use leptos::*;
/// # use leptos_use::utils::MsgpackSerdeCodec;
/// # use codee::binary::MsgpackSerdeCodec;
/// # use leptos_use::{use_websocket, UseWebSocketReturn};
/// # use serde::{Deserialize, Serialize};
/// #
@ -164,7 +164,7 @@ use web_sys::{BinaryType, CloseEvent, Event, MessageEvent, WebSocket};
///
/// ```
/// # use leptos::*;
/// # use leptos_use::utils::FromToStringCodec;
/// # use codee::string::FromToStringCodec;
/// # use leptos_use::{use_websocket, UseWebSocketReturn};
/// # use std::rc::Rc;
/// # #[derive(Clone)]

View file

@ -1,45 +0,0 @@
use crate::utils::{Decoder, Encoder};
/// A codec that relies on `bincode` adn `serde` to encode data in the bincode format.
///
/// This is only available with the **`bincode` feature** enabled.
pub struct BincodeSerdeCodec;
impl<T: serde::Serialize> Encoder<T> for BincodeSerdeCodec {
type Error = bincode::Error;
type Encoded = Vec<u8>;
fn encode(val: &T) -> Result<Self::Encoded, Self::Error> {
bincode::serialize(val)
}
}
impl<T: serde::de::DeserializeOwned> Decoder<T> for BincodeSerdeCodec {
type Error = bincode::Error;
type Encoded = [u8];
fn decode(val: &Self::Encoded) -> Result<T, Self::Error> {
bincode::deserialize(val)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bincode_codec() {
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
struct Test {
s: String,
i: i32,
}
let t = Test {
s: String::from("party time 🎉"),
i: 42,
};
let enc = BincodeSerdeCodec::encode(&t).unwrap();
let dec: Test = BincodeSerdeCodec::decode(&enc).unwrap();
assert_eq!(dec, t);
}
}

View file

@ -1,112 +0,0 @@
use crate::utils::{Decoder, Encoder};
use thiserror::Error;
/// A binary codec that uses rust own binary encoding functions to encode and decode data.
/// This can be used if you want to encode only primitives and don't want to rely on third party
/// crates like `bincode` or `rmp-serde`. If you have more complex data check out
/// [`BincodeSerdeCodec`] or [`MsgpackSerdeCodec`].
pub struct FromToBytesCodec;
#[derive(Error, Debug)]
pub enum FromToBytesCodecError {
#[error("failed to convert byte slice to byte array")]
InvalidByteSlice(#[from] std::array::TryFromSliceError),
#[error("failed to convert byte array to string")]
InvalidString(#[from] std::string::FromUtf8Error),
}
macro_rules! impl_bin_codec_for_number {
($num:ty) => {
impl Encoder<$num> for FromToBytesCodec {
type Error = ();
type Encoded = Vec<u8>;
fn encode(val: &$num) -> Result<Self::Encoded, Self::Error> {
Ok(val.to_be_bytes().to_vec())
}
}
impl Decoder<$num> for FromToBytesCodec {
type Error = FromToBytesCodecError;
type Encoded = [u8];
fn decode(val: &Self::Encoded) -> Result<$num, Self::Error> {
Ok(<$num>::from_be_bytes(val.try_into()?))
}
}
};
}
impl_bin_codec_for_number!(i8);
impl_bin_codec_for_number!(u8);
impl_bin_codec_for_number!(i16);
impl_bin_codec_for_number!(u16);
impl_bin_codec_for_number!(i32);
impl_bin_codec_for_number!(u32);
impl_bin_codec_for_number!(i64);
impl_bin_codec_for_number!(u64);
impl_bin_codec_for_number!(i128);
impl_bin_codec_for_number!(u128);
impl_bin_codec_for_number!(isize);
impl_bin_codec_for_number!(usize);
impl_bin_codec_for_number!(f32);
impl_bin_codec_for_number!(f64);
impl Encoder<bool> for FromToBytesCodec {
type Error = ();
type Encoded = Vec<u8>;
fn encode(val: &bool) -> Result<Self::Encoded, Self::Error> {
let num: u8 = if *val { 1 } else { 0 };
Self::encode(&num)
}
}
impl Decoder<bool> for FromToBytesCodec {
type Error = FromToBytesCodecError;
type Encoded = [u8];
fn decode(val: &Self::Encoded) -> Result<bool, Self::Error> {
let num: u8 = Self::decode(val)?;
Ok(num != 0)
}
}
impl Encoder<String> for FromToBytesCodec {
type Error = ();
type Encoded = Vec<u8>;
fn encode(val: &String) -> Result<Self::Encoded, Self::Error> {
Ok(val.as_bytes().to_vec())
}
}
impl Decoder<String> for FromToBytesCodec {
type Error = FromToBytesCodecError;
type Encoded = [u8];
fn decode(val: &Self::Encoded) -> Result<String, Self::Error> {
Ok(String::from_utf8(val.to_vec())?)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fromtobytes_codec() {
let t = 50;
let enc: Vec<u8> = FromToBytesCodec::encode(&t).unwrap();
let dec: i32 = FromToBytesCodec::decode(enc.as_slice()).unwrap();
assert_eq!(dec, t);
}
}

View file

@ -1,16 +0,0 @@
#[cfg(feature = "bincode_serde")]
mod bincode_serde;
mod from_to_bytes;
#[cfg(feature = "msgpack_serde")]
mod msgpack_serde;
#[cfg(feature = "prost")]
mod prost;
#[cfg(feature = "bincode_serde")]
pub use bincode_serde::*;
#[allow(unused_imports)]
pub use from_to_bytes::*;
#[cfg(feature = "msgpack_serde")]
pub use msgpack_serde::*;
#[cfg(feature = "prost")]
pub use prost::*;

View file

@ -1,45 +0,0 @@
use crate::utils::{Decoder, Encoder};
/// A codec that relies on `rmp-serde` to encode data in the msgpack format.
///
/// This is only available with the **`msgpack` feature** enabled.
pub struct MsgpackSerdeCodec;
impl<T: serde::Serialize> Encoder<T> for MsgpackSerdeCodec {
type Error = rmp_serde::encode::Error;
type Encoded = Vec<u8>;
fn encode(val: &T) -> Result<Self::Encoded, Self::Error> {
rmp_serde::to_vec(val)
}
}
impl<T: serde::de::DeserializeOwned> Decoder<T> for MsgpackSerdeCodec {
type Error = rmp_serde::decode::Error;
type Encoded = [u8];
fn decode(val: &Self::Encoded) -> Result<T, Self::Error> {
rmp_serde::from_slice(val)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_msgpack_codec() {
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
struct Test {
s: String,
i: i32,
}
let t = Test {
s: String::from("party time 🎉"),
i: 42,
};
let enc = MsgpackSerdeCodec::encode(&t).unwrap();
let dec: Test = MsgpackSerdeCodec::decode(&enc).unwrap();
assert_eq!(dec, t);
}
}

View file

@ -1,76 +0,0 @@
use crate::utils::{Decoder, Encoder};
/// A codec for storing ProtoBuf messages that relies on [`prost`](https://github.com/tokio-rs/prost) to parse.
///
/// [Protocol buffers](https://protobuf.dev/overview/) is a serialisation format useful for
/// long-term storage. It provides semantics for versioning that are not present in JSON or other
/// formats. [`prost`] is a Rust implementation of Protocol Buffers.
///
/// This codec uses [`prost`](https://github.com/tokio-rs/prost) to encode the message into a byte stream.
/// To use it with local storage in the example below we wrap it with [`Base64`] to represent the bytes as a string.
///
/// ## Example
/// ```
/// # use leptos::*;
/// # use leptos_use::storage::{StorageType, use_local_storage, use_session_storage, use_storage, UseStorageOptions};
/// # use leptos_use::utils::{Base64, ProstCodec};
/// #
/// # pub fn Demo() -> impl IntoView {
/// // Primitive types:
/// let (get, set, remove) = use_local_storage::<i32, Base64<ProstCodec>>("my-key");
///
/// // Structs:
/// #[derive(Clone, PartialEq, prost::Message)]
/// pub struct MyState {
/// #[prost(string, tag = "1")]
/// pub hello: String,
/// }
/// let (get, set, remove) = use_local_storage::<MyState, Base64<ProstCodec>>("my-struct-key");
/// # view! { }
/// # }
/// ```
///
/// Note: we've defined and used the `prost` attribute here for brevity. Alternate usage would be to
/// describe the message in a .proto file and use [`prost_build`](https://docs.rs/prost-build) to
/// auto-generate the Rust code.
pub struct ProstCodec;
impl<T: prost::Message> Encoder<T> for ProstCodec {
type Error = ();
type Encoded = Vec<u8>;
fn encode(val: &T) -> Result<Self::Encoded, Self::Error> {
let buf = val.encode_to_vec();
Ok(buf)
}
}
impl<T: prost::Message + Default> Decoder<T> for ProstCodec {
type Error = prost::DecodeError;
type Encoded = [u8];
fn decode(val: &Self::Encoded) -> Result<T, Self::Error> {
T::decode(val)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_prost_codec() {
#[derive(Clone, PartialEq, prost::Message)]
struct Test {
#[prost(string, tag = "1")]
s: String,
#[prost(int32, tag = "2")]
i: i32,
}
let t = Test {
s: String::from("party time 🎉"),
i: 42,
};
assert_eq!(ProstCodec::decode(&ProstCodec::encode(&t).unwrap()), Ok(t));
}
}

View file

@ -1,108 +0,0 @@
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)?)
}
}

View file

@ -1,32 +0,0 @@
mod bin;
mod hybrid;
mod string;
pub use bin::*;
pub use hybrid::*;
pub use string::*;
use thiserror::Error;
/// Trait every encoder must implement.
pub trait Encoder<T>: 'static {
type Error;
type Encoded;
fn encode(val: &T) -> Result<Self::Encoded, Self::Error>;
}
/// Trait every decoder must implement.
pub trait Decoder<T>: 'static {
type Error;
type Encoded: ?Sized;
fn decode(val: &Self::Encoded) -> Result<T, Self::Error>;
}
#[derive(Error, Debug)]
pub enum CodecError<E, D> {
#[error("failed to encode: {0}")]
Encode(E),
#[error("failed to decode: {0}")]
Decode(D),
}

View file

@ -1,67 +0,0 @@
use crate::utils::{Decoder, Encoder};
use base64::Engine;
use thiserror::Error;
/// Wraps a binary codec and make it a string codec by representing the binary data as a base64
/// string.
///
/// Only available with the **`base64` feature** enabled.
///
/// Example:
///
/// ```
/// # use leptos_use::utils::{Base64, MsgpackSerdeCodec, Encoder, Decoder};
/// # use serde::{Serialize, Deserialize};
/// #
/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
/// struct MyState {
/// chicken_count: u32,
/// egg_count: u32,
/// farm_name: String,
/// }
///
/// let original_value = MyState {
/// chicken_count: 10,
/// egg_count: 20,
/// farm_name: "My Farm".to_owned(),
/// };
///
/// let encoded: String = Base64::<MsgpackSerdeCodec>::encode(&original_value).unwrap();
/// let decoded: MyState = Base64::<MsgpackSerdeCodec>::decode(&encoded).unwrap();
///
/// assert_eq!(decoded, original_value);
/// ```
pub struct Base64<C>(C);
#[derive(Error, Debug, PartialEq)]
pub enum Base64DecodeError<Err> {
#[error("failed to decode base64: {0}")]
DecodeBase64(#[from] base64::DecodeError),
#[error("failed to decode: {0}")]
Decoder(Err),
}
impl<T, E> Encoder<T> for Base64<E>
where
E: Encoder<T, Encoded = Vec<u8>>,
{
type Error = E::Error;
type Encoded = String;
fn encode(val: &T) -> Result<Self::Encoded, Self::Error> {
Ok(base64::engine::general_purpose::STANDARD.encode(E::encode(val)?))
}
}
impl<T, D> Decoder<T> for Base64<D>
where
D: Decoder<T, Encoded = [u8]>,
{
type Error = Base64DecodeError<D::Error>;
type Encoded = str;
fn decode(val: &Self::Encoded) -> Result<T, Self::Error> {
let buf = base64::engine::general_purpose::STANDARD.decode(val)?;
D::decode(&buf).map_err(Base64DecodeError::Decoder)
}
}

View file

@ -1,51 +0,0 @@
use crate::utils::{Decoder, Encoder};
use std::str::FromStr;
/// A string codec that relies on [`FromStr`] and [`ToString`]. It can encode anything that
/// implements [`ToString`] and decode anything that implements [`FromStr`].
///
/// This makes simple key / value easy to use for primitive types. It is also useful for encoding
/// simply data structures without depending on third party crates like serde and serde_json.
///
/// ## Example
/// ```
/// # use leptos::*;
/// # use leptos_use::storage::{StorageType, use_local_storage, use_session_storage, use_storage, UseStorageOptions};
/// # use leptos_use::utils::FromToStringCodec;
/// #
/// # pub fn Demo() -> impl IntoView {
/// let (get, set, remove) = use_local_storage::<i32, FromToStringCodec>("my-key");
/// # view! { }
/// # }
/// ```
pub struct FromToStringCodec;
impl<T: ToString> Encoder<T> for FromToStringCodec {
type Error = ();
type Encoded = String;
fn encode(val: &T) -> Result<String, Self::Error> {
Ok(val.to_string())
}
}
impl<T: FromStr> Decoder<T> for FromToStringCodec {
type Error = T::Err;
type Encoded = str;
fn decode(val: &Self::Encoded) -> Result<T, Self::Error> {
T::from_str(val)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_string_codec() {
let s = String::from("party time 🎉");
assert_eq!(FromToStringCodec::encode(&s), Ok(s.clone()));
assert_eq!(FromToStringCodec::decode(&s), Ok(s));
}
}

View file

@ -1,67 +0,0 @@
use crate::utils::{Decoder, Encoder};
/// A codec for encoding JSON messages that relies on [`serde_json`].
///
/// Only available with the **`json` feature** enabled.
///
/// ## Example
///
/// ```
/// # use leptos::*;
/// # use leptos_use::storage::{StorageType, use_local_storage, use_session_storage, use_storage, UseStorageOptions};
/// # use serde::{Deserialize, Serialize};
/// # use leptos_use::utils::JsonSerdeCodec;
/// #
/// # pub fn Demo() -> impl IntoView {
/// // Primitive types:
/// let (get, set, remove) = use_local_storage::<i32, JsonSerdeCodec>("my-key");
///
/// // Structs:
/// #[derive(Serialize, Deserialize, Clone, Default, PartialEq)]
/// pub struct MyState {
/// pub hello: String,
/// }
/// let (get, set, remove) = use_local_storage::<MyState, JsonSerdeCodec>("my-struct-key");
/// # view! { }
/// # }
/// ```
pub struct JsonSerdeCodec;
impl<T: serde::Serialize> Encoder<T> for JsonSerdeCodec {
type Error = serde_json::Error;
type Encoded = String;
fn encode(val: &T) -> Result<Self::Encoded, Self::Error> {
serde_json::to_string(val)
}
}
impl<T: serde::de::DeserializeOwned> Decoder<T> for JsonSerdeCodec {
type Error = serde_json::Error;
type Encoded = str;
fn decode(val: &Self::Encoded) -> Result<T, Self::Error> {
serde_json::from_str(val)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_json_codec() {
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
struct Test {
s: String,
i: i32,
}
let t = Test {
s: String::from("party time 🎉"),
i: 42,
};
let enc = JsonSerdeCodec::encode(&t).unwrap();
let dec: Test = JsonSerdeCodec::decode(&enc).unwrap();
assert_eq!(dec, t);
}
}

View file

@ -1,13 +0,0 @@
#[cfg(feature = "base64")]
mod base64;
mod from_to_string;
#[cfg(feature = "json_serde")]
mod json_serde;
mod option;
#[cfg(feature = "base64")]
pub use base64::*;
pub use from_to_string::*;
#[cfg(feature = "json_serde")]
pub use json_serde::*;
pub use option::*;

View file

@ -1,45 +0,0 @@
use crate::utils::{Decoder, Encoder};
/// Wraps a string codec that encodes `T` to create a codec that encodes `Option<T>`.
///
/// Example:
///
/// ```
/// # use leptos_use::utils::{OptionCodec, FromToStringCodec, Encoder, Decoder};
/// #
/// let original_value = Some(4);
/// let encoded = OptionCodec::<FromToStringCodec>::encode(&original_value).unwrap();
/// let decoded = OptionCodec::<FromToStringCodec>::decode(&encoded).unwrap();
///
/// assert_eq!(decoded, original_value);
/// ```
pub struct OptionCodec<C>(C);
impl<T, E> Encoder<Option<T>> for OptionCodec<E>
where
E: Encoder<T, Encoded = String>,
{
type Error = E::Error;
type Encoded = String;
fn encode(val: &Option<T>) -> Result<String, Self::Error> {
match val {
Some(val) => Ok(format!("~<|Some|>~{}", E::encode(val)?)),
None => Ok("~<|None|>~".to_owned()),
}
}
}
impl<T, D> Decoder<Option<T>> for OptionCodec<D>
where
D: Decoder<T, Encoded = str>,
{
type Error = D::Error;
type Encoded = str;
fn decode(str: &Self::Encoded) -> Result<Option<T>, Self::Error> {
str.strip_prefix("~<|Some|>~")
.map(|v| D::decode(v))
.transpose()
}
}

View file

@ -1,4 +1,3 @@
mod codecs;
mod filters;
mod is;
mod js;
@ -7,7 +6,6 @@ mod pausable;
mod signal_filtered;
mod use_derive_signal;
pub use codecs::*;
pub use filters::*;
pub use is::*;
pub(crate) use js_value_from_to_string::*;