derive/src/struct.rs
use darling::*;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{spanned::Spanned, *};
use crate::r#struct::{classify::*, header::parse_header};
pub(crate) mod classify;
pub(crate) mod header;
pub(crate) mod parser;
#[derive(Debug, FromMeta)]
pub(crate) enum LenSpec {
Prefixed { format: Ident },
Slot(Ident),
Remaining,
Embedded,
}
#[derive(Debug, FromMeta)]
pub(crate) struct OptSpec {
#[darling(rename = "if")]
pub condition: Option<Expr>,
#[darling(flatten)]
pub trigger: TriggerSource,
}
#[derive(Debug, FromMeta)]
pub(crate) enum TriggerSource {
Flag(Ident),
}
#[derive(Debug, FromMeta)]
pub(crate) enum ValueSpec {
Format(Ident),
Slot(Ident),
}
#[derive(Debug, FromField)]
#[darling(attributes(wf))]
pub(crate) struct WiredField {
ident: Option<Ident>,
ty: Type,
pub len: Option<LenSpec>,
pub opt: Option<OptSpec>,
pub value: Option<ValueSpec>,
}
impl WiredField {
pub fn no_len(&self) -> syn::Result<()> {
if self.len.is_some() {
return Err(syn::Error::new(
self.ty.span(),
"This type must not have a `len` attribute",
));
}
Ok(())
}
pub fn no_value(&self) -> syn::Result<()> {
if self.value.is_some() {
return Err(syn::Error::new(
self.ty.span(),
"This type must not have a `format` attribute",
));
}
Ok(())
}
pub fn opt_condition(&self) -> syn::Result<(&Expr, &TriggerSource)> {
self.opt
.as_ref()
.ok_or_else(|| syn::Error::new(self.ty.span(), "This type requires an `opt` attribute"))
.map(|opt| {
let OptSpec {
condition: Some(condition),
trigger,
} = opt
else {
return Err(syn::Error::new(
self.ty.span(),
"This type must not have an `opt(if = ..)` attribute",
));
};
Ok((condition, trigger))
})
.flatten()
}
pub fn opt_no_condition(&self) -> syn::Result<&TriggerSource> {
self.opt
.as_ref()
.ok_or_else(|| syn::Error::new(self.ty.span(), "This type requires an `opt` attribute"))
.map(|opt| {
if opt.condition.is_none() {
Ok::<_, syn::Error>(&opt.trigger)
} else {
Err(syn::Error::new(
self.ty.span(),
"This type must not have an `opt(if = ..)` attribute",
))
}
})
.flatten()
}
pub fn len(&self) -> syn::Result<&LenSpec> {
self.len
.as_ref()
.ok_or_else(|| syn::Error::new(self.ty.span(), "This type requires a `len` attribute"))
}
pub fn value(&self) -> syn::Result<&ValueSpec> {
self.value.as_ref().ok_or_else(|| {
syn::Error::new(self.ty.span(), "This type requires a `format` attribute")
})
}
}
#[derive(Debug, FromMeta)]
pub(crate) struct HeaderSpec {
dsl: String,
format: Option<Ident>,
offset: Option<Expr>,
}
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(wf), supports(struct_named, struct_unit))]
pub(crate) struct WiredInput {
ident: Ident,
generics: Generics,
data: ast::Data<darling::util::Ignored, WiredField>,
header: Option<HeaderSpec>,
align: Option<Ident>,
}
pub fn expand(input: &WiredInput) -> syn::Result<TokenStream> {
let ident = &input.ident;
let mut generics = input.generics.clone();
let lt = match generics.lifetimes().next() {
Some(lt) => lt.lifetime.clone(),
None => {
let lt = Lifetime::new("'a", proc_macro2::Span::call_site());
generics
.params
.insert(0, GenericParam::Lifetime(LifetimeParam::new(lt.clone())));
lt
}
};
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
let (impl_generics, _, _) = generics.split_for_impl();
let fields = input
.data
.as_ref()
.take_struct()
.expect("supports(struct_named) guarantees a struct");
let ((hty, consts), hc, ho) = if let Some(h) = &input.header {
let hc = if let Some(fmt) = &h.format {
quote!(elvwf::scalar::#fmt)
} else {
quote!(elvwf::scalar::Be)
};
let ho = if let Some(offset) = &h.offset {
quote!(+ #offset)
} else {
quote!()
};
(parse_header(&h.dsl)?, hc, ho)
} else {
(
(
quote!(()),
quote!(
const HBASE: () = ();
),
),
quote!(elvwf::scalar::Be),
quote!(),
)
};
let mut len = Vec::<TokenStream>::with_capacity(fields.len());
let mut header = Vec::<TokenStream>::with_capacity(fields.len());
let mut encode = Vec::<TokenStream>::with_capacity(fields.len());
let mut decode = Vec::<TokenStream>::with_capacity(fields.len());
let mut idents = Vec::<_>::with_capacity(fields.len());
let mut where_msg = Vec::<TokenStream>::with_capacity(fields.len());
let mut remaining = fields.len();
for f in fields.iter() {
remaining -= 1;
let (ty, stripped) = classify(&f.ty)?;
if let FieldKind::Optional(FieldShape::Msg(ty)) | FieldKind::Required(FieldShape::Msg(ty)) =
ty
{
where_msg.push(quote!(#ty: elvwf::Wired<#lt>));
}
let parsed = parser::ParsedField::new(f, stripped, remaining == 0)?;
len.push(parsed.len);
header.push(parsed.header);
encode.push(parsed.encode);
decode.push(parsed.decode);
idents.push(parsed.ident);
}
let (l, e, c) = if let Some(align) = &input.align {
(
quote!(len += (core::mem::size_of::<#align>() - len % core::mem::size_of::<#align>()) % core::mem::size_of::<#align>();),
quote!(elvwf::slice::encode_value::<&[u8]>(buf, &(&[0; 8])[..(core::mem::size_of::<#align>() - (_bs - buf.len()) % core::mem::size_of::<#align>()) % core::mem::size_of::<#align>()])?;),
quote!(let _ = elvwf::slice::decode_value::<&[u8]>(buf, (core::mem::size_of::<#align>() - (_bs - buf.len()) % core::mem::size_of::<#align>()) % core::mem::size_of::<#align>() as usize)?;),
)
} else {
(quote!(), quote!(), quote!())
};
let where_clause: WhereClause = if let Some(wc) = where_clause {
parse_quote! {
#wc
#(#where_msg),*
}
} else {
parse_quote! {
where
#(#where_msg),*
}
};
Ok(quote! {
impl #impl_generics #ident #ty_generics #where_clause {
#consts
}
#[allow(unused_braces)]
impl #impl_generics elvwf::Wired<#lt> for #ident #ty_generics #where_clause {
type Header = #hty;
type HeaderCodec = #hc;
fn body_len(&self) -> usize {
let mut len: usize = 0;
#(#len)*
#l
len
}
fn header(&self) -> Result<Self::Header, elvwf::Error> {
let mut h: Self::Header = Self::HBASE;
#(#header)*
Ok(h #ho)
}
fn encode_body(self, buf: &mut &mut [u8]) -> Result<(), elvwf::Error> {
let _bs: usize = buf.len();
#(#encode)*
#e
Ok(())
}
fn decode_body(buf: &mut &#lt[u8], len: usize, h: Self::Header) -> Result<Self, elvwf::Error> {
let _bs: usize = buf.len();
#(#decode)*
#c
Ok(Self {
#(#idents),*
})
}
}
})
}