zenobuf-macros 0.3.2

Procedural macros for the Zenobuf framework - derive macros for Protocol Buffer integration
Documentation
//! # Zenobuf Macros - Procedural macros for the Zenobuf framework
//!
//! This crate provides procedural macros that simplify working with the Zenobuf framework,
//! particularly for integrating Protocol Buffer messages with the type-safe messaging system.
//!
//! ## Overview
//!
//! The main macro provided is [`ZenobufMessage`], which automatically implements the
//! [`zenobuf_core::Message`] trait for Protocol Buffer messages generated by Prost.
//!
//! ## Quick Start
//!
//! ### 1. Add to your `Cargo.toml`
//!
//! ```toml
//! [dependencies]
//! zenobuf-core = "0.2"
//! zenobuf-macros = "0.2"
//! prost = "0.13"
//!
//! [build-dependencies]
//! prost-build = "0.13"
//! ```
//!
//! ### 2. Setup automatic derive in `build.rs`
//!
//! ```rust,ignore
//! fn main() -> std::io::Result<()> {
//!     prost_build::Config::new()
//!         .type_attribute(".", "#[derive(zenobuf_macros::ZenobufMessage)]")
//!         .compile_protos(&["protos/messages.proto"], &["protos"])?;
//!     Ok(())
//! }
//! ```
//!
//! ### 3. Use in your code
//!
//! ```rust,ignore
//! // Generated protobuf code automatically has ZenobufMessage derived
//! pub mod proto {
//!     include!(concat!(env!("OUT_DIR"), "/my_messages.rs"));
//! }
//!
//! use zenobuf_core::Node;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//!     let node = Node::new("my_node").await?;
//!
//!     // Your protobuf messages now work seamlessly with Zenobuf
//!     let publisher = node
//!         .publisher::<proto::MyMessage>("topic")
//!         .build()
//!         .await?;
//!
//!     Ok(())
//! }
//! ```
//!
//! ## Manual Usage
//!
//! You can also manually derive the macro on your own types:
//!
//! ```rust,ignore
//! use zenobuf_macros::ZenobufMessage;
//!
//! #[derive(Clone, PartialEq, Default, ZenobufMessage)]
//! pub struct MyMessage {
//!     pub value: i32,
//! }
//! ```
//!
//! ## What the Macro Does
//!
//! The `ZenobufMessage` derive macro implements the [`zenobuf_core::Message`] trait,
//! which provides:
//! - Type name information for debugging and introspection
//! - Integration with Zenobuf's type-safe messaging system
//! - Automatic serialization/deserialization support
//!
//! ## Requirements
//!
//! Types that derive `ZenobufMessage` must also implement:
//! - `Clone`
//! - `PartialEq`
//! - `Default`
//! - `prost::Message` (for Protocol Buffer types)
//! - `Send + Sync + 'static` (automatically satisfied by most types)

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

/// Derives the [`zenobuf_core::Message`] trait for Protocol Buffer messages
///
/// This macro automatically implements the [`zenobuf_core::Message`] trait for Protocol
/// Buffer messages generated by Prost. It extracts the type name from the struct name
/// and implements the required methods for integration with Zenobuf's messaging system.
///
/// # Requirements
///
/// The type must implement:
/// - `Clone`
/// - `PartialEq`
/// - `Default`
/// - `prost::Message` (for Protocol Buffer types)
/// - `Send + Sync + 'static`
///
/// # Examples
///
/// ## Manual Usage
///
/// ```rust,ignore
/// use zenobuf_macros::ZenobufMessage;
///
/// #[derive(Clone, PartialEq, Default, ZenobufMessage)]
/// pub struct Point {
///     pub x: f32,
///     pub y: f32,
///     pub z: f32,
/// }
/// ```
///
/// ## With Protocol Buffers (typical usage)
///
/// In your `build.rs`:
/// ```rust,ignore
/// prost_build::Config::new()
///     .type_attribute(".", "#[derive(zenobuf_macros::ZenobufMessage)]")
///     .compile_protos(&["protos/messages.proto"], &["protos"])?;
/// # Ok::<(), std::io::Error>(())
/// ```
///
/// This automatically adds the derive macro to all generated Protocol Buffer types.
///
/// ## Using with Zenobuf
///
/// ```rust,ignore
/// use zenobuf_core::Node;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
///     let node = Node::new("publisher").await?;
///
///     // Point now implements Message and can be used with publishers
///     let publisher = node
///         .publisher::<Point>("points")
///         .build()
///         .await?;
///
///     let point = Point { x: 1.0, y: 2.0, z: 3.0 };
///     publisher.publish(&point)?;
///
///     Ok(())
/// }
/// ```
///
/// # Generated Implementation
///
/// The macro generates an implementation like this:
///
/// ```rust,ignore
/// impl zenobuf_core::Message for Point {
///     fn type_name() -> &'static str {
///         "Point"
///     }
/// }
/// ```
#[proc_macro_derive(ZenobufMessage)]
pub fn derive_zenobuf_message(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let expanded = quote! {
        impl #impl_generics ::zenobuf_core::Message for #name #ty_generics #where_clause {
            fn type_name() -> &'static str {
                stringify!(#name)
            }
        }
    };

    TokenStream::from(expanded)
}