Skip to main content

miden_protocol_macros/
lib.rs

1//! Procedural macros for the Miden project.
2//!
3//! Provides derive macros and other procedural macros to reduce boilerplate
4//! and ensure consistency across the Miden codebase.
5//!
6//! ## Available Macros
7//!
8//! ### `WordWrapper`
9//!
10//! A derive macro for tuple structs wrapping a `Word` type. Automatically generates
11//! accessor methods and `From` trait implementations.
12
13use proc_macro::TokenStream;
14use quote::quote;
15use syn::{Data, DeriveInput, Fields, Type, parse_macro_input};
16
17/// Generates accessor methods for tuple structs wrapping a `Word` type.
18///
19/// Automatically implements:
20/// - `new_unchecked(Word) -> Self` - Construct without further checks
21/// - `as_elements(&self) -> &[Felt]` - Returns the elements representation
22/// - `as_bytes(&self) -> [u8; 32]` - Returns the byte representation
23/// - `to_hex(&self) -> String` - Returns a big-endian, hex-encoded string
24/// - `as_word(&self) -> Word` - Returns the underlying Word
25///
26/// Note: This macro does NOT generate `From` trait implementations. If you need conversions
27/// to/from `Word` or `[u8; 32]`, implement them manually for your type.
28///
29/// # Example
30///
31/// ```ignore
32/// use miden_protocol_macros::WordWrapper;
33/// use miden_crypto::word::Word;
34///
35/// #[derive(WordWrapper)]
36/// pub struct NoteId(Word);
37/// ```
38///
39/// This will generate implementations equivalent to:
40///
41/// ```ignore
42/// impl NoteId {
43///     /// Construct without further checks from a given `Word`
44///     ///
45///     /// # Warning
46///     ///
47///     /// This requires the caller to uphold the guarantees/invariants of this type (if any).
48///     /// Check the type-level documentation for guarantees/invariants.
49///     pub fn new_unchecked(word: Word) -> Self {
50///         Self(word)
51///     }
52///
53///     pub fn as_elements(&self) -> &[Felt] {
54///         self.0.as_elements()
55///     }
56///
57///     pub fn as_bytes(&self) -> [u8; 32] {
58///         self.0.as_bytes()
59///     }
60///
61///     pub fn to_hex(&self) -> String {
62///         self.0.to_hex()
63///     }
64///
65///     pub fn as_word(&self) -> Word {
66///         self.0
67///     }
68/// }
69/// ```
70#[proc_macro_derive(WordWrapper)]
71pub fn word_wrapper_derive(input: TokenStream) -> TokenStream {
72    let input = parse_macro_input!(input as DeriveInput);
73
74    let name = &input.ident;
75    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
76
77    // Validate that this is a tuple struct with a single field
78    let field_type = match &input.data {
79        Data::Struct(data_struct) => match &data_struct.fields {
80            Fields::Unnamed(fields) if fields.unnamed.len() == 1 => match fields.unnamed.first() {
81                Some(field) => &field.ty,
82                None => {
83                    return syn::Error::new_spanned(
84                        &input,
85                        "WordWrapper requires exactly one field",
86                    )
87                    .to_compile_error()
88                    .into();
89                },
90            },
91            _ => {
92                return syn::Error::new_spanned(
93                    &input,
94                    "WordWrapper can only be derived for tuple structs with exactly one field",
95                )
96                .to_compile_error()
97                .into();
98            },
99        },
100        _ => {
101            return syn::Error::new_spanned(&input, "WordWrapper can only be derived for structs")
102                .to_compile_error()
103                .into();
104        },
105    };
106
107    // Verify that the field type is 'Word' (or a path ending in 'Word')
108    if let Type::Path(type_path) = field_type {
109        let last_segment = type_path.path.segments.last();
110        if let Some(segment) = last_segment {
111            if segment.ident != "Word" {
112                return syn::Error::new_spanned(
113                    field_type,
114                    "WordWrapper can only be derived for types wrapping a 'Word' field",
115                )
116                .to_compile_error()
117                .into();
118            }
119        } else {
120            return syn::Error::new_spanned(
121                field_type,
122                "WordWrapper can only be derived for types wrapping a 'Word' field",
123            )
124            .to_compile_error()
125            .into();
126        }
127    } else {
128        return syn::Error::new_spanned(
129            field_type,
130            "WordWrapper can only be derived for types wrapping a 'Word' field",
131        )
132        .to_compile_error()
133        .into();
134    }
135
136    let expanded = quote! {
137        impl #impl_generics #name #ty_generics #where_clause {
138            /// Construct without further checks from a given `Word`
139            ///
140            /// # Warning
141            ///
142            /// This requires the caller to uphold the guarantees/invariants of this type (if any).
143            /// Check the type-level documentation for guarantees/invariants.
144            pub fn from_raw(word: Word) -> Self {
145                Self(word)
146            }
147
148            /// Returns the elements representation of this value.
149            pub fn as_elements(&self) -> &[Felt] {
150                self.0.as_elements()
151            }
152
153            /// Returns the byte representation of this value.
154            pub fn as_bytes(&self) -> [u8; 32] {
155                self.0.as_bytes()
156            }
157
158            /// Returns a big-endian, hex-encoded string.
159            pub fn to_hex(&self) -> String {
160                self.0.to_hex()
161            }
162
163            /// Returns the underlying word of this value.
164            pub fn as_word(&self) -> Word {
165                self.0
166            }
167        }
168    };
169
170    TokenStream::from(expanded)
171}