photon_ring_derive/lib.rs
1// Copyright 2026 Photon Ring Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Derive macro for [`photon_ring::Pod`].
5//!
6//! ```ignore
7//! #[derive(photon_ring::Pod)]
8//! struct Quote {
9//! price: f64,
10//! volume: u32,
11//! }
12//! ```
13//!
14//! This generates `#[repr(C)]`, `#[derive(Clone, Copy)]`, and
15//! `unsafe impl photon_ring::Pod for Quote {}` — with a compile-time
16//! check that every field type implements `Pod`.
17
18use proc_macro::TokenStream;
19use quote::quote;
20use syn::{parse_macro_input, Data, DeriveInput, Fields};
21
22/// Derive `Pod` for a struct.
23///
24/// Requirements:
25/// - Must be a struct (not enum or union).
26/// - All fields must implement `Pod`.
27/// - The struct will be given `#[repr(C)]` semantics (the macro adds
28/// `Clone` and `Copy` derives and the `unsafe impl Pod`).
29///
30/// # Example
31///
32/// ```ignore
33/// #[derive(photon_ring::Pod)]
34/// struct Tick {
35/// price: f64,
36/// volume: u32,
37/// _pad: u32,
38/// }
39/// ```
40#[proc_macro_derive(Pod)]
41pub fn derive_pod(input: TokenStream) -> TokenStream {
42 let input = parse_macro_input!(input as DeriveInput);
43 let name = &input.ident;
44 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
45
46 // Only structs are supported
47 let fields = match &input.data {
48 Data::Struct(s) => match &s.fields {
49 Fields::Named(f) => f.named.iter().collect::<Vec<_>>(),
50 Fields::Unnamed(f) => f.unnamed.iter().collect::<Vec<_>>(),
51 Fields::Unit => vec![],
52 },
53 _ => {
54 return syn::Error::new_spanned(&input.ident, "Pod can only be derived for structs")
55 .to_compile_error()
56 .into();
57 }
58 };
59
60 // Generate compile-time assertions that every field is Pod
61 let field_assertions = fields.iter().map(|f| {
62 let ty = &f.ty;
63 quote! {
64 const _: () = {
65 fn _assert_pod<T: photon_ring::Pod>() {}
66 fn _check() { _assert_pod::<#ty>(); }
67 };
68 }
69 });
70
71 let expanded = quote! {
72 // Compile-time field checks
73 #(#field_assertions)*
74
75 // Safety: all fields verified to be Pod via compile-time assertions above.
76 // The derive macro only applies to structs, and Pod requires that every
77 // bit pattern is valid — which holds when all fields are Pod.
78 unsafe impl #impl_generics photon_ring::Pod for #name #ty_generics #where_clause {}
79 };
80
81 TokenStream::from(expanded)
82}