Skip to content

Commit

Permalink
move to strum
Browse files Browse the repository at this point in the history
  • Loading branch information
dandyvica committed Aug 4, 2024
1 parent 4bfa4f3 commit 30a044f
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 56 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ either = "1.9.0"
bytes = "1.5.0"

[dev-dependencies]
enum_from = { git = "https://github.com/dandyvica/enum_from.git" }
num_enum = "0.7.3"
serde = { version = "1.0.195", features = [ "derive" ] }


[[example]]
name = "ntp"
src = "examples/ntp.rs"
Expand Down
34 changes: 18 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Traits and procedural macros to convert structures and enums into bigendian data streams.
//! Traits and procedural macros to convert structures and enums into `bigendian` data streams.
//!
//! It's used to send struct or enum data to the wire, or receive them from the wire.
//! The trait is defined as:
Expand All @@ -18,7 +18,7 @@
//! }
//! ```
//!
//! It's using the [`byteorder`](https://docs.rs/byteorder/latest/byteorder) crate in order to convert integers or floats to a bigendian buffer of ```u8```.
//! It's extensively using the [`byteorder`](https://docs.rs/byteorder/latest/byteorder) crate in order to convert integers or floats to a bigendian buffer of ```u8```.
//! It is compatible with other attributes like those provided by [`serde`](https://crates.io/crates/serde) crate.
//!
//! ## How to use it ?
Expand All @@ -29,15 +29,17 @@
//! * ```#[derive(ToNetwork)]``` to auto-implement the ```FromNetworkOrder``` trait
//! * ```#[derive(ToNetwork, FromNetwork)]``` to auto-implement the ```ToNetworkOrder``` & ```FromNetworkOrder``` traits
//!
//! The ```FromNetworkOrder``` is only supported for C-like enums. For the ```ToNetworkOrder``` trait on C-like enums, it needs to be ```Copy, Clone```.
//! The ```ToNetworkOrder``` trait is supported for all structs or enums containing supported types (see below for a list of supported types).
//!
//! The ```FromNetworkOrder``` trait is only supported for C-like unit-only enums. For the ```ToNetworkOrder``` trait on C-like enums, it needs to be ```Copy, Clone```.
//!
//! ## The ```#[deser]``` attribute
//! ## The ```#[from_network]``` field attribute
//! In addition it's possible to add a field attribute on a struct's field for the ```FromNetworkOrder``` trait:
//!
//! * ```#[deser(ignore)]``` : the field is not deserialized.
//! * ```#[deser(debug)]``` : a ```dbg!(self.field_name)``` statement is inserted after the field is being deserialized.
//! * ```#[deser(with_fn(func))]``` : the function ```func(&mut self) -> std::io::Result<()>``` is called for that field.
//! * ```#[deser(with_code(code))]``` : the ```code``` block is injected before the field is being deserialized.
//! * ```#[from_network(ignore)]``` : the field is not deserialized.
//! * ```#[from_network(debug)]``` : a ```dbg!(self.field_name)``` statement is inserted after the field is being deserialized.
//! * ```#[from_network(with_fn(func))]``` : the function ```func(&mut self) -> std::io::Result<()>``` is called for that field.
//! * ```#[from_network(with_code(block))]``` : the ```code``` block is injected before the field is being deserialized.
//!
//! # Examples
//!
Expand All @@ -49,12 +51,12 @@
//! y: u16,
//!
//! // last field is not deserialized
//! #[deser(ignore)]
//! #[from_network(ignore)]
//! z: u16,
//! }
//!
//! // this function will be called for z field
//! fn update(p: &mut PointFn) {
//! fn update(p: &mut PointFn) -> std::io::Result<()> {
//! p.z = 3;
//! }
//!
Expand All @@ -64,7 +66,7 @@
//! y: u16,
//!
//! // last field is not deserialized
//! #[deser(with_fn(update))]
//! #[from_network(with_fn(update))]
//! z: u16,
//! }
//!
Expand All @@ -75,7 +77,7 @@
//! y: u16,
//!
//! // last field is not deserialized
//! #[deser(with_code(self.z = 0xFFFF;))]
//! #[from_network(with_code(self.z = 0xFFFF;))]
//! z: u16,
//! }
//! ```
Expand Down Expand Up @@ -107,12 +109,12 @@
//!
//! ## Examples
//! Two examples can be found in the ```examples``` directory:
//!
//!
//! * ntp
//! * dns
//!
//! In general, you should add the ```#[derive(ToNetwork, FromNetwork)]``` derives to benefit from the procedural macros which automatically convert structure to network order back and forth. They are included using:
//!
//!
//! In general, you should add the ```#[derive(ToNetwork, FromNetwork)]``` derive macros to benefit from the procedural macros which automatically convert structure to network order back and forth. They are included using:
//!
//! ```rust
//! use type2network::{FromNetworkOrder, ToNetworkOrder};
//! use type2network_derive::{FromNetwork, ToNetwork};
Expand Down
28 changes: 17 additions & 11 deletions tests/integration_tests.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use num_enum::{FromPrimitive, TryFromPrimitive};

use serde::Serialize;
// some tests for structs
use type2network::{FromNetworkOrder, ToNetworkOrder};
use type2network_derive::{FromNetwork, ToNetwork};

use enum_from::EnumTryFrom;
// use enum_from::EnumTryFrom;

// used for boiler plate unit tests for integers, floats etc
pub fn to_network_test<T: ToNetworkOrder>(val: &T, size: usize, v: &[u8]) {
Expand Down Expand Up @@ -108,6 +109,7 @@ fn struct_lifetime_to() {
}

#[test]
#[allow(dead_code)]
fn struct_lifetime_from() {
#[derive(Debug, PartialEq, FromNetwork)]
struct DataLifeTimeWithTypeParam<'a, T, V>
Expand Down Expand Up @@ -167,7 +169,8 @@ fn enum_c_like() {
#[test]
#[allow(dead_code)]
fn enum_simple() {
#[derive(Debug, Default, PartialEq, Copy, Clone, ToNetwork, FromNetwork, EnumTryFrom)]
#[derive(Debug, Default, PartialEq, Copy, Clone, TryFromPrimitive, ToNetwork, FromNetwork)]
#[from_network(TryFrom)]
#[repr(u64)]
enum Color {
#[default]
Expand All @@ -185,7 +188,8 @@ fn enum_simple() {
#[test]
#[allow(dead_code)]
fn enum_opcode() {
#[derive(Debug, Copy, Clone, PartialEq, EnumTryFrom, ToNetwork, FromNetwork)]
#[derive(Debug, Copy, Clone, PartialEq, ToNetwork, FromNetwork, FromPrimitive)]
#[from_network(From)]
#[repr(u16)]
pub enum OpCodeReserved {
Query = 0, //[RFC1035]
Expand All @@ -196,7 +200,7 @@ fn enum_opcode() {
Update = 5, // [RFC2136]
DOS = 6, // DNS Stateful Operations (DSO) [RFC8490]

#[fallback]
#[num_enum(catch_all)]
Reserved(u16),
}

Expand All @@ -206,6 +210,8 @@ fn enum_opcode() {
}
}



let op = OpCodeReserved::IQuery;
to_network_test(&op, 2, &[0, 1]);
let op = OpCodeReserved::Reserved(55);
Expand Down Expand Up @@ -258,7 +264,7 @@ fn struct_attr_no() {
y: u16,

// last field is not deserialized
#[deser(ignore)]
#[from_network(ignore)]
z: u16,
}

Expand All @@ -284,7 +290,7 @@ fn struct_attr_fn() {
y: u16,

// last field is not deserialized
#[deser(with_fn(update))]
#[from_network(with_fn(update))]
z: u16,
}

Expand All @@ -304,7 +310,7 @@ fn struct_attr_code() {
y: u16,

// last field is not deserialized
#[deser(with_code(self.z = 0xFFFF;))]
#[from_network(with_code(self.z = 0xFFFF;))]
z: u16,
}

Expand All @@ -320,10 +326,10 @@ fn struct_attr_code() {
fn struct_debug() {
#[derive(Debug, Default, PartialEq, ToNetwork, FromNetwork)]
struct PointDebug {
#[deser(debug)]
#[from_network(debug)]
x: u16,

#[deser(debug)]
#[from_network(debug)]
y: u16,
}
}
Expand All @@ -333,10 +339,10 @@ fn struct_serde() {
#[derive(Debug, Default, PartialEq, ToNetwork, FromNetwork, Serialize)]
struct PointDebug {
#[serde(skip_serializing)]
#[deser(debug)]
#[from_network(debug)]
x: u16,

#[deser(debug)]
#[from_network(debug)]
y: u16,
}
}
80 changes: 69 additions & 11 deletions type2network_derive/src/enum/from.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{DataEnum, DeriveInput};
use syn::{Attribute, DataEnum, DeriveInput};

use syn_utils::*;

Expand All @@ -10,24 +10,44 @@ impl EnumDeriveBuilder {
pub fn from_network(ast: &DeriveInput, _de: &DataEnum) -> proc_macro2::TokenStream {
let enum_name = &ast.ident;
let enum_string = enum_name.to_string();

// which trait does the enum implement ? From or TryFrom or none of these ?
let implemented_trait = get_from_or_tryfrom(&ast.attrs);

let ty = SynUtils::repr_size(&ast.attrs)
.unwrap_or_else(|| unimplemented!("repr size is mandatory on enum {}", enum_name));

let value_expr = build_value(&ty);

quote! {
impl<'a> FromNetworkOrder<'a> for #enum_name {
fn deserialize_from(&mut self, buffer: &mut std::io::Cursor<&'a [u8]>) -> std::io::Result<()> {
#value_expr
match <#enum_name>::try_from(value) {
Ok(ct) => {
*self = ct;
Ok(())
// the implementation of FromNetworkOrder depends on whether From or TryFrom is implemented
match implemented_trait {
TryFromOrFrom::From => quote! {
impl<'a> FromNetworkOrder<'a> for #enum_name {
fn deserialize_from(&mut self, buffer: &mut std::io::Cursor<&'a [u8]>) -> std::io::Result<()> {
#value_expr
*self = <#enum_name>::from(value);
Ok(())
}
}
},
TryFromOrFrom::TryFrom => quote! {
impl<'a> FromNetworkOrder<'a> for #enum_name {
fn deserialize_from(&mut self, buffer: &mut std::io::Cursor<&'a [u8]>) -> std::io::Result<()> {
#value_expr
match <#enum_name>::try_from(value) {
Ok(ct) => {
*self = ct;
Ok(())
}
_ => Err(std::io::Error::other(format!("error converting value '{}' to enum type {}", value, #enum_string))),
}
_ => Err(std::io::Error::other(format!("error converting value '{}' to enum type {}", value, #enum_string))),
}
}
}
},
TryFromOrFrom::None => panic!(
"at least, '{}' should implement From or TryFrom trait",
enum_string
),
}
}
}
Expand All @@ -43,6 +63,44 @@ fn build_value(ty: &TokenStream) -> proc_macro2::TokenStream {
}
}

// FromNetwork for enums makes it mandatory to impl either From or TryFrom
// This is hinted using the #[from_network(From)] ou #[from_network(TryFrom)] outer attribute
enum TryFromOrFrom {
From,
TryFrom,
None,
}

fn get_from_or_tryfrom(attrs: &[Attribute]) -> TryFromOrFrom {
let mut result = TryFromOrFrom::None;

// loop through attributes
for attr in attrs {
// we found the #[tonetwork] attributes
if attr.path().is_ident("from_network") {
attr.parse_nested_meta(|meta| {
// #[from_network(From)]
if meta.path.is_ident("From") {
result = TryFromOrFrom::From;
return Ok(());
}

// #[from_network(TryFrom)]
if meta.path.is_ident("TryFrom") {
result = TryFromOrFrom::TryFrom;
return Ok(());
}

// neither From nor TryFrom was found
return Ok(());
})
.unwrap();
}
}

result
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
5 changes: 1 addition & 4 deletions type2network_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ use struct_builder::{StructBuilder, StructDeriveBuilder};
mod r#enum;
use r#enum::{EnumBuilder, EnumDeriveBuilder};

// mod enum_builder;
// use enum_builder::{EnumBuilder, EnumDeriveBuilder};

mod generics;

#[proc_macro_derive(ToNetwork)]
Expand All @@ -23,7 +20,7 @@ pub fn to_network(input: TokenStream) -> TokenStream {
)
}

#[proc_macro_derive(FromNetwork, attributes(deser))]
#[proc_macro_derive(FromNetwork, attributes(from_network))]
pub fn from_network(input: TokenStream) -> TokenStream {
derive_helper(
input,
Expand Down
Loading

0 comments on commit 30a044f

Please sign in to comment.