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}