elvwf

8 commits
Updated 2026-06-13 11:19:44
derive/src
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),*
                })
            }
        }
    })
}