# shmp
[](https://crates.io/crates/shmp)
[](https://docs.rs/shmp)
[](./LICENSE-MIT)
A flexible and efficient MessagePack implementation in Rust, designed with `serde` integration in mind.
`shmp` offers two primary ways to work with MessagePack data:
1. **Direct Serialization with Serde**: Serialize and deserialize your Rust structs directly to and from MessagePack bytes for maximum performance and type safety. (This is the default and recommended approach).
2. **Dynamic `Pack` Enum**: Work with a dynamic, `serde_json::Value`-like enum (`shmp::Pack`) to represent MessagePack data when the schema is unknown at compile time.
## Features
* **Serde Integration**: Robust support for `serde` allows for easy serialization and deserialization of custom types.
* **Dynamic `Pack` Type**: A flexible enum for representing and manipulating any valid MessagePack data.
* **`pack!` Macro**: An intuitive, JSON-like macro for ergonomic creation of `Pack` values.
* **Zero-Copy Deserialization**: Efficiently deserialize `&str` and `&[u8]` from a byte slice without unnecessary allocations.
* **Extensible**: Provides `EncodeExt` and `DecodeExt` traits to easily support custom MessagePack extension types.
* **Optional Features**:
* `chrono`: Adds support for serializing/deserializing `chrono::DateTime<Utc>` and `chrono::NaiveDateTime` as MessagePack Timestamps.
## Installation
Add `shmp` to your `Cargo.toml`:
```toml
[dependencies]
shmp = "0.1.0"
```
To enable optional features like `chrono`:
```toml
[dependencies]
shmp = { version = "0.1.0", features = ["chrono"] }
```
## Quick Start
### 1. Using Serde (Recommended)
This is the most common and idiomatic way to use `shmp`.
```rust
use serde::{Serialize, Deserialize};
use shmp::{to_vec, from_slice, to_writer, from_reader, to_pack, from_pack, Pack};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct User {
id: u32,
username: String,
is_active: bool,
}
fn main() -> Result<(), shmp::Error> {
let user = User {
id: 1024,
username: "shmp_user".to_string(),
is_active: true,
};
// 1. Direct serialization to/from bytes
let encoded: Vec<u8> = to_vec(&user)?;
let decoded: User = from_slice(&encoded)?;
assert_eq!(user, decoded);
println!("Successfully round-tripped via bytes: {:?}", decoded);
// 2. Serialization to/from an I/O stream (like a file or network socket)
let mut buffer: Vec<u8> = Vec::new();
to_writer(&mut buffer, &user)?;
let mut reader = std::io::Cursor::new(&buffer);
let decoded_from_reader: User = from_reader(&mut reader)?;
assert_eq!(user, decoded_from_reader);
println!("Successfully round-tripped via reader/writer: {:?}", decoded_from_reader);
// 3. Using the `Pack` enum as an intermediate representation
// Struct -> Pack -> Bytes
let encoded_pack: Pack = to_pack(&user)?; // Pass a reference, which also works.
let encoded_from_pack: Vec<u8> = to_vec(&encoded_pack)?;
// Bytes -> Pack -> Struct
let decoded_pack: Pack = from_slice(&encoded_from_pack)?;
let decoded_from_pack: User = from_pack(decoded_pack)?;
assert_eq!(user, decoded_from_pack);
println!("Successfully round-tripped via Pack enum: {:?}", decoded_from_pack);
return Ok(());
}
```
### 2. Using the Dynamic `Pack` Enum
Useful when the structure is not known at compile time.
```rust
use shmp::{to_vec, from_slice, from_pack, pack, Pack};
use serde::Deserialize;
fn main() -> Result<(), shmp::Error> {
let manual_pack = Pack::Map(vec![
(Pack::String("id".into()), Pack::Uint(12345u64)),
(Pack::String("username".into()), Pack::String("shmp".into())),
(Pack::String("is_active".into()), Pack::Boolean(true)),
]);
// Manually creating a `Pack` value can be verbose.
// The `pack!` macro provides a much cleaner, JSON-like syntax for the same result.
let macro_pack = pack!({
"id": 12345u32,
"username": "shmp",
"is_active": true
});
assert_eq!(manual_pack, macro_pack);
println!("Successfully created Pack value with the pack! macro.");
// The macro-created value can be serialized and deserialized like any other `Pack`.
let encoded = to_vec(¯o_pack)?;
let decoded: Pack = from_slice(&encoded)?;
assert_eq!(macro_pack, decoded);
assert_eq!(manual_pack, macro_pack, "Both methods produce the same Pack value");
// It can also be deserialized directly into a struct.
#[derive(Deserialize, Debug, PartialEq)]
struct User {
id: u32,
username: String,
is_active: bool,
}
// It can also be deserialized directly into a struct.
let user = User {
id: 12345u32,
username: "shmp".to_string(),
is_active: true,
};
let macro_user: User = from_pack(macro_pack)?;
assert_eq!(user, macro_user);
println!("Successfully created and round-tripped User value with the pack! macro.");
Ok(())
}
```
## License
This project is dual-licensed under either of
* Apache License, Version 2.0, (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
* MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
at your option.