shmp

Crates.ioshmp
lib.rsshmp
version0.1.0
created_at2025-10-10 09:59:26.409214+00
updated_at2025-10-10 09:59:26.409214+00
descriptionA flexible and efficient MessagePack serialization implementation in Rust
homepage
repositoryhttps://github.com/shanpark/shmp
max_upload_size
id1876744
size349,121
(shanpark)

documentation

README

shmp

crates.io docs.rs License: MIT OR Apache-2.0

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:

[dependencies]
shmp = "0.1.0"

To enable optional features like chrono:

[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.

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.

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(&macro_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

at your option.

Commit count: 0

cargo fmt