Add JSON versioning docs + examples

This commit is contained in:
Joshua McQuistan 2023-11-04 15:44:09 +00:00
parent 7c3ebf09f4
commit 808e97c072

View file

@ -21,6 +21,98 @@ use super::Codec;
/// # view! { }
/// # }
/// ```
///
/// ## Versioning
///
/// If the JSON decoder fails, the storage hook will return `T::Default` dropping the stored JSON value. See [`Codec`](super::Codec) for general information on codec versioning.
///
/// ### Rely on serde
/// This codec uses [`serde_json`] under the hood. A simple way to avoid complex versioning is to rely on serde's [field attributes](https://serde.rs/field-attrs.html) such as [`serde(default)`](https://serde.rs/field-attrs.html#default) and [`serde(rename = "...")`](https://serde.rs/field-attrs.html#rename).
///
/// ### String replacement
/// Previous versions of leptos-use offered a `merge_defaults` fn to rewrite the encoded value. This is possible by wrapping the codec but should be avoided.
///
/// ```
/// # use leptos::*;
/// # use leptos_use::storage::{StorageType, use_local_storage, use_session_storage, use_storage_with_options, UseStorageOptions, Codec, JsonCodec};
/// # use serde::{Deserialize, Serialize};
/// #
/// # pub fn Demo() -> impl IntoView {
/// #[derive(Serialize, Deserialize, Clone, Default, PartialEq)]
/// pub struct MyState {
/// pub hello: String,
/// pub greeting: String,
/// }
///
/// #[derive(Clone, Default)]
/// pub struct MyStateCodec();
/// impl Codec<MyState> for MyStateCodec {
/// type Error = serde_json::Error;
///
/// fn encode(&self, val: &MyState) -> Result<String, Self::Error> {
/// serde_json::to_string(val)
/// }
///
/// fn decode(&self, stored_value: String) -> Result<MyState, Self::Error> {
/// let default_value = MyState::default();
/// let rewritten = if stored_value.contains(r#""greeting":"#) {
/// stored_value
/// } else {
/// // add "greeting": "Hello" to the string
/// stored_value.replace("}", &format!(r#""greeting": "{}"}}"#, default_value.greeting))
/// };
/// serde_json::from_str(&rewritten)
/// }
/// }
///
/// let (get, set, remove) = use_local_storage::<MyState, MyStateCodec>("my-struct-key");
/// # view! { }
/// # }
/// ```
///
/// ### Transform a `JsValue`
/// A better alternative to string replacement might be to parse the JSON then transform the resulting `JsValue` before decoding it to to your struct again.
///
/// ```
/// # use leptos::*;
/// # use leptos_use::storage::{StorageType, use_local_storage, use_session_storage, use_storage_with_options, UseStorageOptions, Codec, JsonCodec};
/// # use serde::{Deserialize, Serialize};
/// # use serde_json::json;
/// #
/// # pub fn Demo() -> impl IntoView {
/// #[derive(Serialize, Deserialize, Clone, Default, PartialEq)]
/// pub struct MyState {
/// pub hello: String,
/// pub greeting: String,
/// }
///
/// #[derive(Clone, Default)]
/// pub struct MyStateCodec();
/// impl Codec<MyState> for MyStateCodec {
/// type Error = serde_json::Error;
///
/// fn encode(&self, val: &MyState) -> Result<String, Self::Error> {
/// serde_json::to_string(val)
/// }
///
/// fn decode(&self, stored_value: String) -> Result<MyState, Self::Error> {
/// let mut val: serde_json::Value = serde_json::from_str(&stored_value)?;
/// // add "greeting": "Hello" to the object if it's missing
/// if let Some(obj) = val.as_object_mut() {
/// if !obj.contains_key("greeting") {
/// obj.insert("greeting".to_string(), json!("Hello"));
/// }
/// serde_json::from_value(val)
/// } else {
/// Ok(MyState::default())
/// }
/// }
/// }
///
/// let (get, set, remove) = use_local_storage::<MyState, MyStateCodec>("my-struct-key");
/// # view! { }
/// # }
/// ```
#[derive(Clone, Default, PartialEq)]
pub struct JsonCodec();