1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::quote;
6use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Member, Result, Type};
7
8struct StructField<'a> {
16 member: Member,
17 ty: &'a Type,
18 skip: bool,
21}
22
23fn struct_fields<'a>(input: &'a DeriveInput, derive: &str) -> Result<Vec<StructField<'a>>> {
28 let Data::Struct(data) = &input.data else {
29 return Err(syn::Error::new_spanned(
30 &input.ident,
31 format!("{derive} can only be derived for structs"),
32 ));
33 };
34
35 data.fields
36 .iter()
37 .enumerate()
38 .map(|(index, field)| {
39 let member = field
40 .ident
41 .clone()
42 .map_or_else(|| Member::Unnamed(syn::Index::from(index)), Member::Named);
43 Ok(StructField {
44 member,
45 ty: &field.ty,
46 skip: has_skip_attribute(&field.attrs)?,
47 })
48 })
49 .collect()
50}
51
52fn bounded_types<'a>(fields: &'a [StructField<'a>]) -> Vec<&'a Type> {
58 fields
59 .iter()
60 .filter(|field| !field.skip)
61 .map(|field| field.ty)
62 .collect()
63}
64
65fn defaulted_types<'a>(fields: &'a [StructField<'a>]) -> Vec<&'a Type> {
68 fields
69 .iter()
70 .filter(|field| field.skip)
71 .map(|field| field.ty)
72 .collect()
73}
74
75fn impl_block(
78 input: &DeriveInput,
79 trait_path: &TokenStream2,
80 bounded: &[&Type],
81 defaulted: &[&Type],
82 items: &TokenStream2,
83) -> TokenStream2 {
84 let name = &input.ident;
85 let mut generics = input.generics.clone();
86
87 if !bounded.is_empty() {
88 let where_clause = generics.make_where_clause();
89 for ty in bounded {
90 where_clause.predicates.push(parse_quote!(#ty: #trait_path));
91 }
92 }
93 if !defaulted.is_empty() {
94 let where_clause = generics.make_where_clause();
95 for ty in defaulted {
96 where_clause
97 .predicates
98 .push(parse_quote!(#ty: ::core::default::Default));
99 }
100 }
101
102 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
103 quote! {
104 impl #impl_generics #trait_path for #name #ty_generics #where_clause {
105 #items
106 }
107 }
108}
109
110fn field_repr_size(field_type: &Type) -> TokenStream2 {
118 quote! {
119 ::core::mem::size_of::<<#field_type as ::spongefish::FromUniform>::Repr>()
120 }
121}
122
123fn from_uniform_field_expr(field_type: &Type) -> TokenStream2 {
131 quote! {
132 {
133 let mut field_buf = <#field_type as ::spongefish::FromUniform>::Repr::default();
134 let field_size = ::core::convert::AsMut::<[u8]>::as_mut(&mut field_buf).len();
135 let start = offset;
136 let end = start + field_size;
137 assert!(
138 end <= bytes.len(),
139 "`FromUniform` derive: field representation is wider than the derived buffer; \
140 `Repr` must satisfy `size_of::<Repr>() == Repr::default().as_mut().len()`"
141 );
142 ::core::convert::AsMut::<[u8]>::as_mut(&mut field_buf)
143 .copy_from_slice(&bytes[start..end]);
144 offset = end;
145 <#field_type as ::spongefish::FromUniform>::from_uniform(field_buf)
146 }
147 }
148}
149
150fn generate_encoding_impl(input: &DeriveInput) -> Result<TokenStream2> {
151 let fields = struct_fields(input, "Encoding")?;
152 let bounded = bounded_types(&fields);
153
154 let field_encodings = fields.iter().filter(|field| !field.skip).map(|field| {
155 let member = &field.member;
156 quote! {
157 output.extend_from_slice(self.#member.encode().as_ref());
158 }
159 });
160
161 let trait_path = quote!(::spongefish::Encoding);
162 Ok(impl_block(
163 input,
164 &trait_path,
165 &bounded,
166 &[],
167 "e! {
168 fn encode(&self) -> impl AsRef<[u8]> {
169 let mut output = ::spongefish::__private::Vec::with_capacity(
176 ::core::mem::size_of::<Self>(),
177 );
178 #(#field_encodings)*
179 output
180 }
181 },
182 ))
183}
184
185fn generate_from_uniform_impl(input: &DeriveInput) -> Result<TokenStream2> {
186 let fields = struct_fields(input, "FromUniform")?;
187 let bounded = bounded_types(&fields);
188 let defaulted = defaulted_types(&fields);
189
190 let field_inits = fields.iter().map(|field| {
191 let member = &field.member;
192 if field.skip {
193 return quote!(#member: Default::default(),);
194 }
195 let from_uniform_field = from_uniform_field_expr(field.ty);
196 quote!(#member: #from_uniform_field,)
197 });
198
199 let size_components = bounded.iter().copied().map(field_repr_size);
200 let size_calc = if bounded.is_empty() {
201 quote!(0usize)
202 } else {
203 quote!(#(#size_components)+*)
204 };
205
206 let body = if bounded.is_empty() {
210 quote! {
211 let _ = buf;
212 Self { #(#field_inits)* }
213 }
214 } else {
215 quote! {
216 let bytes = buf.as_ref();
219 let mut offset = 0usize;
220 let value = Self { #(#field_inits)* };
221 assert_eq!(
222 offset,
223 bytes.len(),
224 "`FromUniform` derive: field representations do not cover the derived buffer; \
225 every `Repr` must satisfy `size_of::<Repr>() == Repr::default().as_mut().len()`"
226 );
227 value
228 }
229 };
230
231 let trait_path = quote!(::spongefish::FromUniform);
232 Ok(impl_block(
233 input,
234 &trait_path,
235 &bounded,
236 &defaulted,
237 "e! {
238 type Repr = ::spongefish::ByteArray<{ #size_calc }>;
239
240 fn from_uniform(buf: Self::Repr) -> Self {
241 #body
242 }
243 },
244 ))
245}
246
247fn generate_from_narg_impl(input: &DeriveInput) -> Result<TokenStream2> {
248 let fields = struct_fields(input, "FromNarg")?;
249 let bounded = bounded_types(&fields);
250 let defaulted = defaulted_types(&fields);
251
252 let field_inits = fields.iter().map(|field| {
253 let member = &field.member;
254 if field.skip {
255 return quote!(#member: Default::default(),);
256 }
257 let field_type = field.ty;
258 quote! {
259 #member: ::spongefish::NargReader::read::<#field_type>(reader)?,
260 }
261 });
262
263 let trait_path = quote!(::spongefish::FromNarg);
264 Ok(impl_block(
265 input,
266 &trait_path,
267 &bounded,
268 &defaulted,
269 "e! {
270 fn from_narg(
271 reader: &::spongefish::NargReader<'_>,
272 ) -> ::core::result::Result<Self, ::spongefish::VerificationError> {
273 let _ = &reader;
276 Ok(Self { #(#field_inits)* })
277 }
278 },
279 ))
280}
281
282fn generate_unit_impl(input: &DeriveInput) -> Result<TokenStream2> {
283 let fields = struct_fields(input, "Unit")?;
284 let bounded = bounded_types(&fields);
285
286 let zero_fields = fields.iter().map(|field| {
287 let member = &field.member;
288 if field.skip {
289 return quote!(#member: ::core::default::Default::default(),);
290 }
291 let field_type = field.ty;
292 quote!(#member: <#field_type as ::spongefish::Unit>::ZERO,)
293 });
294
295 let trait_path = quote!(::spongefish::Unit);
296 Ok(impl_block(
297 input,
298 &trait_path,
299 &bounded,
300 &[],
301 "e! {
302 const ZERO: Self = Self { #(#zero_fields)* };
303 },
304 ))
305}
306
307fn expand(generated: Result<TokenStream2>) -> TokenStream {
310 TokenStream::from(generated.unwrap_or_else(syn::Error::into_compile_error))
311}
312
313#[proc_macro_derive(Encoding, attributes(spongefish))]
341pub fn derive_encoding(input: TokenStream) -> TokenStream {
342 let input = parse_macro_input!(input as DeriveInput);
343 expand(generate_encoding_impl(&input))
344}
345
346#[proc_macro_derive(FromUniform, attributes(spongefish))]
351pub fn derive_from_uniform(input: TokenStream) -> TokenStream {
352 let input = parse_macro_input!(input as DeriveInput);
353 expand(generate_from_uniform_impl(&input))
354}
355
356#[proc_macro_derive(FromNarg, attributes(spongefish))]
363pub fn derive_from_narg(input: TokenStream) -> TokenStream {
364 let input = parse_macro_input!(input as DeriveInput);
365 expand(generate_from_narg_impl(&input))
366}
367
368#[proc_macro_derive(Codec, attributes(spongefish))]
372pub fn derive_codec(input: TokenStream) -> TokenStream {
373 let input = parse_macro_input!(input as DeriveInput);
374 expand((|| {
375 let encoding = generate_encoding_impl(&input)?;
376 let from_uniform = generate_from_uniform_impl(&input)?;
377 let from_narg = generate_from_narg_impl(&input)?;
378 Ok(quote! {
379 #encoding
380 #from_uniform
381 #from_narg
382 })
383 })())
384}
385
386#[proc_macro_derive(Unit, attributes(spongefish))]
402pub fn derive_unit(input: TokenStream) -> TokenStream {
403 let input = parse_macro_input!(input as DeriveInput);
404 expand(generate_unit_impl(&input))
405}
406
407fn has_skip_attribute(attrs: &[syn::Attribute]) -> Result<bool> {
422 for attr in attrs {
423 if !attr.path().is_ident("spongefish") {
424 continue;
425 }
426
427 let mut skip = false;
428 attr.parse_nested_meta(|meta| {
429 if meta.path.is_ident("skip") {
430 skip = true;
431 Ok(())
432 } else {
433 Err(meta.error("unknown `spongefish` option; expected `skip`"))
434 }
435 })?;
436
437 if !skip {
438 return Err(syn::Error::new_spanned(
439 attr,
440 "empty `#[spongefish(..)]`: write `#[spongefish(skip)]` or remove the attribute",
441 ));
442 }
443 return Ok(true);
444 }
445
446 Ok(false)
447}