Skip to content

This project implements custom Serialize and Deserialize traits with procedural macros that automatically generate serialization code for structs containing integer fields.

Notifications You must be signed in to change notification settings

codewithmide/custom-macro

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

Custom Serialize/Deserialize Macro

A Rust procedural macro implementation for automatic serialization and deserialization of structs containing integer fields.

Project Structure

custom-macro/
├── serialize_macro/          # Procedural macro implementation
│   ├── src/lib.rs           # Main macro definitions
│   └── Cargo.toml           # Macro dependencies
├── serialize_macro_traits/   # Trait definitions
│   ├── src/lib.rs           # Serialize/Deserialize traits
│   └── Cargo.toml           # Trait dependencies
├── app/                     # Example application
│   ├── src/main.rs          # Demo usage
│   └── Cargo.toml           # App dependencies
└── README.md                # This file

Overview

This project implements custom Serialize and Deserialize traits with procedural macros that automatically generate serialization code for structs containing integer fields.

Features

  • Automatic Serialization: Converts struct fields to bytes using big-endian encoding
  • Automatic Deserialization: Reconstructs structs from byte arrays
  • Type Safety: Compile-time code generation ensures type safety
  • Error Handling: Built-in error handling for malformed data

Components

1. Traits (serialize_macro_traits)

Defines the core traits:

pub trait Serialize {
    fn serialize(&self) -> Vec<u8>;
}

pub trait Deserialize {
    fn deserialize(data: &[u8]) -> Result<Self, Error> 
    where 
        Self: Sized;
}

#[derive(Debug)]
pub struct Error;

2. Procedural Macros (serialize_macro)

Two derive macros:

  • #[derive(SerializeNumberStruct)] - Generates Serialize implementation
  • #[derive(DeserializeNumberStruct)] - Generates Deserialize implementation

How It Works

Serialization: Each integer field is converted to bytes using to_be_bytes() and concatenated.

Deserialization: Bytes are read in 4-byte chunks, converted back to i32 using from_be_bytes().

Usage

1. Define Your Struct

use serialize_macro::{SerializeNumberStruct, DeserializeNumberStruct};
use serialize_macro_traits::{Serialize, Deserialize};

#[derive(SerializeNumberStruct, DeserializeNumberStruct)]
struct MyStruct {
    field1: i32,
    field2: i32,
    field3: i32,
}

2. Serialize Data

let my_data = MyStruct {
    field1: 42,
    field2: 100,
    field3: -50,
};

let bytes = my_data.serialize();
println!("Serialized: {:?}", bytes);

3. Deserialize Data

match MyStruct::deserialize(&bytes) {
    Ok(restored) => println!("Deserialized: {:?}", restored),
    Err(e) => println!("Error: {:?}", e),
}

Generated Code Example

For a struct like:

#[derive(SerializeNumberStruct)]
struct Point {
    x: i32,
    y: i32,
}

The macro generates:

impl Serialize for Point {
    fn serialize(&self) -> Vec<u8> {
        let mut result = Vec::new();
        result.extend_from_slice(&self.x.to_be_bytes());
        result.extend_from_slice(&self.y.to_be_bytes());
        result
    }
}

Limitations

  • Integer Fields Only: Currently supports only i32 fields
  • Named Fields Only: Requires structs with named fields (not tuple structs)
  • Fixed Size: Each field is assumed to be 4 bytes (i32)
  • No Nested Structs: Does not handle nested struct serialization

Building and Running

Build the Project

# Build all components
cargo build

# Build specific crate
cd serialize_macro
cargo build

Run the Example

cd app
cargo run

Expected Output

Original: Swap { a: 5, b: 10 }
Serialized: [0, 0, 0, 5, 0, 0, 0, 10]
Deserialized: Ok(Swap { a: 5, b: 10 })

Technical Details

Macro Implementation

The macros use:

  • syn crate for parsing Rust syntax
  • quote crate for generating code
  • proc_macro for the macro interface

Serialization Format

  • Byte Order: Big-endian (network byte order)
  • Field Order: Fields are serialized in declaration order
  • Size: Each i32 field takes exactly 4 bytes

Error Handling

Deserialization can fail if:

  • Input buffer is too short
  • Byte slice cannot be converted to array

Dependencies

serialize_macro

[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["full"] }

[lib]
proc-macro = true

serialize_macro_traits

# No external dependencies

app

[dependencies]
serialize_macro = { path = "../serialize_macro" }
serialize_macro_traits = { path = "../serialize_macro_traits" }

Contributing

To extend this macro:

  1. Add Type Support: Modify field handling to support other integer types
  2. Add Validation: Implement field type checking
  3. Add Features: Support for enums, nested structs, or different encodings
  4. Improve Errors: Add more descriptive error messages

License

This project is for educational purposes demonstrating Rust procedural macro implementation.

About

This project implements custom Serialize and Deserialize traits with procedural macros that automatically generate serialization code for structs containing integer fields.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages