Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document rmp-serde and the serde pitfalls #83

Merged
merged 3 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 31 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ See [concepts](#concepts) for more details.

## Goals

- **Interoperability**: Allows different applications to work together, even if they are using different
- **Interoperability**: Allows different applications to work together, even if they are using different
versions of the data model.
- **Data Consistency**: Ensure that we process the data expected model.
- **Flexibility**: You can use any serialization format you want. More details [here](#setup-your-serialization-format).
Expand Down Expand Up @@ -39,10 +39,10 @@ let bytes = native_model::encode(&dot).unwrap();
// Application 2
// We are able to decode the bytes directly into a new type DotV2 (upgrade).
let (mut dot, source_version) = native_model::decode::<DotV2>(bytes).unwrap();
assert_eq!(dot, DotV2 {
name: "".to_string(),
x: 1,
y: 2
assert_eq!(dot, DotV2 {
name: "".to_string(),
x: 1,
y: 2
});
dot.name = "Dot".to_string();
dot.x = 5;
Expand Down Expand Up @@ -73,19 +73,39 @@ changes, the default serialization format is the oldest one.
- `bincode_1_3`: [bincode](https://docs.rs/bincode/1.3.3/bincode/) v1.3 (default)
- `bincode_2_rc`: [bincode](https://docs.rs/bincode/2.0.0-rc.3/bincode/) v2.0.0-rc3
- `postcard_1_0`: [postcard](https://docs.rs/postcard/1.0.0/postcard/) v1.0
- `rpm_serde_1_3`: [rmp-serde](https://docs.rs/rmp-serde/1.3.0/rmp_serde/) v1.3

### Custom serialization format

Define a struct with the name you want. This struct must implement [`native_model::Encode`](https://docs.rs/native_model/latest/native_model/trait.Encode.html) and [`native_model::Decode`](https://docs.rs/native_model/latest/native_model/trait.Decode.html) traits.

Full examples:
Full examples:
- [bincode with encode/decode](./tests_crate/tests/example/custom_codec/bincode.rs)
- [bincode with serde](./tests_crate/tests/example/custom_codec/bincode_serde.rs)

Others examples, see the default implementations:
- [bincode v1.3](./src/codec/bincode_1_3.rs)
- [bincode v2.0 (rc)](./src/codec/bincode_2_rc.rs)
- [postcard v1.0](./src/codec/postcard_1_0.rs)
- [bincode v2.0 (rc)](./src/codec/bincode_2_rc.rs)
- [postcard v1.0](./src/codec/postcard_1_0.rs)
- [rmp-serde v1.3](./src/codec/rmp_serde_1_3.rs)

### Notice
`native_model` provides implementations that rely on metadata-less formats and `serde`.
There are known issues with some `serde` advanced features such as:
vincent-herlemont marked this conversation as resolved.
Show resolved Hide resolved

- `#[serde(flatten)]`
- `#[serde(skip)]`
- `#[serde(skip_deserializing)]`
- `#[serde(skip_serializing)]`
- `#[serde(skip_serializing_if = "path")]`
- `#[serde(tag = "...")]`
- `#[serde(untagged)]`

Or types implementing similar strategies such as [`serde_json::Value`][serde_json_value].

The `rmp-serde` serialization format can optionally support them serializing structs as maps, the `RmpSerdeNamed` struct is provided to support this use-case.

[serde_json_value]: https://docs.rs/serde_json/latest/serde_json/enum.Value.html

## Data model

Expand Down Expand Up @@ -189,9 +209,9 @@ Early development. Not ready for production.

In order to understand how the native model works, you need to understand the following concepts.

- **Identity**(`id`): The identity is the unique identifier of the model. It is used to identify the model and
- **Identity**(`id`): The identity is the unique identifier of the model. It is used to identify the model and
prevent to decode a model into the wrong Rust type.
- **Version**(`version`) The version is the version of the model. It is used to check the compatibility between two
- **Version**(`version`) The version is the version of the model. It is used to check the compatibility between two
models.
- **Encode**: The encode is the process of converting a model into a byte array.
- **Decode**: The decode is the process of converting a byte array into a model.
Expand All @@ -212,7 +232,7 @@ Full example [here](tests/example/example_define_model.rs).

Native model has
been designed to have a minimal and constant overhead. That means that the overhead is the same
whatever the size of the data. Under the hood we use the [zerocopy](https://docs.rs/zerocopy/latest/zerocopy/) crate
whatever the size of the data. Under the hood we use the [zerocopy](https://docs.rs/zerocopy/latest/zerocopy/) crate
to avoid unnecessary copies.

👉 To know the total time of the encode/decode, you need to add the time of your serialization format.
Expand Down
50 changes: 48 additions & 2 deletions src/codec/rmp_serde_1_3.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
//! [rmp-serde 1.3](https://crates.io/crates/rmp-serde/1.3.0) ·
//! Enable the `rmp_serde_1_3` feature and
//! [`annotate your type`](crate::native_model) with
//! `native_model::rmp_serde_1_3::RmpSerde` to have `native_db` use this crate.
//! `native_model::rmp_serde_1_3::RmpSerde` or `native_model::rmp_serde_1_3::RmpSerdeNamed`
//! to have `native_db` use this crate.

/// Used to specify the
/// [rmp-serde 1.3](https://crates.io/crates/rmp-serde/1.3.0)
/// crate for serialization & deserialization.
/// crate for serialization & deserialization, using arrays to serialize structs.
///
/// Do not use this if you plan to use serde features that skip serializing fields,
/// use [RmpSerdeNamed] instead.
///
/// # Basic usage
///
Expand Down Expand Up @@ -43,3 +47,45 @@ impl<T: for<'de> serde::Deserialize<'de>> crate::Decode<T> for RmpSerde {
rmp_serde_1_3::decode::from_slice(&data)
}
}

/// Used to specify the
/// [rmp-serde 1.3](https://crates.io/crates/rmp-serde/1.3.0)
/// crate for serialization & deserialization, using maps to serialize structs.
///
/// # Basic usage
///
/// After enabling the `rmp_serde_1_3` feature in your `Cargo.toml`, use the
/// [`with`](crate::native_model) attribute on your type to instruct
/// `native_model` to use `RmpSerdeNamed` for serialization & deserialization.
///
/// Example usage:
///
/// ```rust
/// # use native_model::*;
/// #[derive(Clone, Default, serde::Deserialize, serde::Serialize)]
/// #[native_model(id = 1, version = 1, with = native_model::rmp_serde_1_3::RmpSerdeNamed)]
/// struct MyStruct {
/// #[serde(skip_serializing_if = "String::is_empty")]
/// my_string: String
/// }
/// ```

pub struct RmpSerdeNamed;

#[cfg(all(feature = "serde", feature = "rmp_serde_1_3"))]
impl<T: serde::Serialize> crate::Encode<T> for RmpSerdeNamed {
type Error = rmp_serde_1_3::encode::Error;
/// Serializes a type into bytes using the `rmp-serde` `1.3` crate.
fn encode(obj: &T) -> Result<Vec<u8>, Self::Error> {
rmp_serde_1_3::encode::to_vec_named(obj)
}
}

#[cfg(all(feature = "serde", feature = "rmp_serde_1_3"))]
impl<T: for<'de> serde::Deserialize<'de>> crate::Decode<T> for RmpSerdeNamed {
type Error = rmp_serde_1_3::decode::Error;
/// Deserializes a type from bytes using the `rmp-serde` `1.3` crate.
fn decode(data: Vec<u8>) -> Result<T, Self::Error> {
rmp_serde_1_3::decode::from_slice(&data)
}
}
Loading