Skip to content
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
[workspace]
members = [
"msgpacker",
"msgpacker-bench"
"msgpacker-bench",
"msgpacker-derive"
]

[profile.bench]
Expand Down
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,49 @@ We have two main structures available:
* Message - Owned parsed values
* MessageRef - Message parsed by reference and bound to the lifetime of the readers source

For convenience, a derive macro is available to implement `Packable` and `Unpackable` for the types. These implementations will allow the types to be sent and received from `MessagePacker` and `MessageUnpacker` implementations, such as `CursorPacker`.

## Example

```rust
use msgpacker::prelude::*;

#[derive(MsgPacker, Debug, Clone, PartialEq, Eq)]
pub struct Foo {
val: u64,
text: String,
flag: bool,
bar: Bar,
}

#[derive(MsgPacker, Debug, Clone, PartialEq, Eq)]
pub struct Bar {
arr: [u8; 32],
}

let bar = Bar { arr: [0xff; 32] };
let foo = Foo {
val: 15,
text: String::from("Hello, world!"),
flag: true,
bar,
};

// Create a new bytes buffer
let mut buffer: Vec<u8> = vec![];

// Pack the message into the buffer
CursorPacker::new(&mut buffer).pack(foo.clone()).expect("failed to pack `Foo`");

// Unpack the message from the buffer
let foo_p = CursorPacker::new(&buffer).unpack::<Foo>().expect("failed to unpack `Foo`");

// Assert the unpacked message is exactly the same as the original
assert_eq!(foo, foo_p);
```

## Example of manual implementation

```rust
use msgpacker::prelude::*;
use std::io::{Cursor, Seek};
Expand Down
18 changes: 18 additions & 0 deletions msgpacker-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "msgpacker-derive"
version = "0.1.0"
authors = ["Victor Lopez <victor@codx.io>"]
categories = ["compression", "encoding", "parser-implementations"]
edition = "2021"
keywords = ["messagepack", "msgpack"]
license = "MIT/Apache-2.0"
readme = "README.md"
repository = "https://github.com/codx-dev/msgpacker"
description = "Derive macros for the MessagePack protocol implementation for Rust."

[lib]
proc-macro = true

[dependencies]
quote = "1.0"
syn = { version = "1.0", features = ["full"] }
1 change: 1 addition & 0 deletions msgpacker-derive/LICENSE-APACHE
1 change: 1 addition & 0 deletions msgpacker-derive/LICENSE-MIT
1 change: 1 addition & 0 deletions msgpacker-derive/README.md
78 changes: 78 additions & 0 deletions msgpacker-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#![crate_type = "proc-macro"]
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::{
parse_macro_input, parse_quote, Block, Data, DeriveInput, FieldValue, Fields, Member, Token,
};

#[proc_macro_derive(MsgPacker)]
pub fn msg_packer(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);

let name = input.ident;
let data = input.data;

let mut values: Punctuated<FieldValue, Token![,]> = Punctuated::new();
let block: Block = match data {
Data::Struct(syn::DataStruct {
struct_token: _,
fields: Fields::Named(f),
semi_token: _,
}) => f
.named
.into_pairs()
.map(|p| p.into_value())
.fold(syn::parse_str("{}").unwrap(), |mut block, field| {
let ident = field.ident.as_ref().cloned().unwrap();
let ty = field.ty;

block.stmts.push(parse_quote! {
n += <#ty as msgpacker::prelude::Packable>::pack(&self.#ident, packer.by_ref())?;
});

let fv = FieldValue {
attrs: vec![],
member: Member::Named(ident.clone()),
colon_token: Some(<Token![:]>::default()),
expr: parse_quote! {
<#ty as msgpacker::prelude::Unpackable>::unpack(unpacker.by_ref())?
},
};
values.push(fv);

block
}),
_ => todo!(),
};

let expanded = quote! {
impl msgpacker::prelude::Packable for #name {
fn pack<W>(&self, mut packer: W) -> std::io::Result<usize>
where
W: std::io::Write
{
let mut n = 0;

#block

Ok(n)
}
}

impl msgpacker::prelude::Unpackable for #name {
fn unpack<R>(mut unpacker: R) -> std::io::Result<Self>
where
R: std::io::BufRead,
{
Ok(Self {
#values
})
}
}
};

TokenStream::from(expanded)
}
7 changes: 6 additions & 1 deletion msgpacker/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "msgpacker"
version = "0.1.7"
version = "0.2.0"
authors = ["Victor Lopez <victor@codx.io>"]
categories = ["compression", "encoding", "parser-implementations"]
edition = "2021"
Expand All @@ -11,3 +11,8 @@ repository = "https://github.com/codx-dev/msgpacker"
description = "MessagePack protocol implementation for Rust."

[dependencies]
msgpacker-derive = { version = "0.1", optional = true }

[features]
default = ["derive"]
derive = ["msgpacker-derive"]
5 changes: 5 additions & 0 deletions msgpacker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod integer;
mod map;
mod message;
mod message_ref;
mod packer;

pub use message::Message;
pub use message_ref::MessageRef;
Expand All @@ -26,5 +27,9 @@ pub mod types {
pub mod prelude {
pub use crate::message::Message;
pub use crate::message_ref::MessageRef;
pub use crate::packer::{CursorPacker, MessagePacker, MessageUnpacker, Packable, Unpackable};
pub use crate::types::*;

#[cfg(feature = "derive")]
pub use msgpacker_derive::MsgPacker;
}
132 changes: 0 additions & 132 deletions msgpacker/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -763,138 +763,6 @@ impl Message {
}
}

impl From<Integer> for Message {
fn from(i: Integer) -> Self {
Self::Integer(i)
}
}

impl From<u8> for Message {
fn from(i: u8) -> Self {
Self::Integer(Integer::unsigned(i))
}
}

impl From<u16> for Message {
fn from(i: u16) -> Self {
Self::Integer(Integer::unsigned(i))
}
}

impl From<u32> for Message {
fn from(i: u32) -> Self {
Self::Integer(Integer::unsigned(i))
}
}

impl From<u64> for Message {
fn from(i: u64) -> Self {
Self::Integer(Integer::unsigned(i))
}
}

impl From<i8> for Message {
fn from(i: i8) -> Self {
Self::Integer(Integer::signed(i))
}
}

impl From<i16> for Message {
fn from(i: i16) -> Self {
Self::Integer(Integer::signed(i))
}
}

impl From<i32> for Message {
fn from(i: i32) -> Self {
Self::Integer(Integer::signed(i))
}
}

impl From<i64> for Message {
fn from(i: i64) -> Self {
Self::Integer(Integer::signed(i))
}
}

impl From<bool> for Message {
fn from(b: bool) -> Self {
Self::Boolean(b)
}
}

impl From<Float> for Message {
fn from(f: Float) -> Self {
Self::Float(f)
}
}

impl From<f32> for Message {
fn from(f: f32) -> Self {
Self::Float(Float::f32(f))
}
}

impl From<f64> for Message {
fn from(f: f64) -> Self {
Self::Float(Float::f64(f))
}
}

impl From<&str> for Message {
fn from(s: &str) -> Self {
Self::String(s.to_owned())
}
}

impl From<String> for Message {
fn from(s: String) -> Self {
Self::String(s)
}
}

impl From<Vec<u8>> for Message {
fn from(b: Vec<u8>) -> Self {
Self::Bin(b)
}
}

impl FromIterator<u8> for Message {
fn from_iter<I: IntoIterator<Item = u8>>(iter: I) -> Self {
iter.into_iter().collect::<Vec<u8>>().into()
}
}

impl From<Vec<Message>> for Message {
fn from(a: Vec<Message>) -> Self {
Self::Array(a)
}
}

impl FromIterator<Message> for Message {
fn from_iter<I: IntoIterator<Item = Message>>(iter: I) -> Self {
iter.into_iter().collect::<Vec<Message>>().into()
}
}

impl From<Vec<MapEntry>> for Message {
fn from(m: Vec<MapEntry>) -> Self {
Self::Map(m)
}
}

impl FromIterator<MapEntry> for Message {
fn from_iter<I: IntoIterator<Item = MapEntry>>(iter: I) -> Self {
iter.into_iter().collect::<Vec<MapEntry>>().into()
}
}

impl From<Extension> for Message {
fn from(e: Extension) -> Self {
Self::Extension(e)
}
}

impl<M: Into<Message>> Index<M> for Message {
type Output = Message;

Expand Down
Loading