dos-uid-derive 5.0.1

dos-actors UniqueIdentifier derive macro
Documentation
use quote::quote;
use syn::{
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    DeriveInput, Ident, LitInt, Token, Type,
};

use crate::{Expand, Expanded};

/// ALIAS attributes
///
/// #[alias(name = <type>, client = <type>, traits = <Write,|Read,|Size>)]
#[derive(Debug, Clone, Default)]
pub struct Attributes {
    name: Option<Type>,
    client: Option<Type>,
    traits: Vec<Ident>,
    port: Option<syn::LitInt>,
    pub skip_uid: bool,
}

impl Parse for Attributes {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let mut alias_attrs: Attributes = Default::default();
        while let Ok(key) = input.parse::<Ident>() {
            let _ = input.parse::<Token!(=)>()?;
            if key == "name" {
                alias_attrs.name = input.parse::<Type>().ok();
            }
            if key == "client" {
                alias_attrs.client = input.parse::<Type>().ok();
            }
            if key == "traits" {
                alias_attrs.traits =
                    Punctuated::<Ident, Token![,]>::parse_separated_nonempty(input)?
                        .into_iter()
                        .collect();
            }
            if key == "port" {
                let _ = input.parse::<LitInt>().map(|port| {
                    alias_attrs.port = Some(port);
                });
            }
            let Ok(_) = input.parse::<Token!(,)>() else {
                return Ok(alias_attrs);
            };
        }
        Ok(alias_attrs)
    }
}

impl Expand for Attributes {
    fn expand(&self, input: &DeriveInput) -> Expanded {
        let Self {
            name,
            client,
            traits,
            skip_uid,
            port,
        } = self;
        let DeriveInput {
            ident, generics, ..
        } = input;
        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
        let uid = if *skip_uid {
            quote!()
        } else {
            if let Some(port) = port {
                quote! {
                    impl #impl_generics ::interface::UniqueIdentifier for #ident #ty_generics #where_clause {
                        const PORT: u16 = #port;
                        type DataType = <#name as ::interface::UniqueIdentifier>::DataType;
                    }
                }
            } else {
                quote! {
                    impl #impl_generics ::interface::UniqueIdentifier for #ident #ty_generics #where_clause {
                        const PORT: u16 = <#name as ::interface::UniqueIdentifier>::PORT;
                        type DataType = <#name as ::interface::UniqueIdentifier>::DataType;
                    }
                }
            }
        };
        let mut write = quote!();
        let mut read = quote!();
        let mut size = quote!();
        for a_trait in traits {
            if a_trait.to_string().as_str().to_lowercase() == "write" {
                write = quote! {
                    impl #impl_generics ::interface::Write<#ident #ty_generics> for #client {
                        #[inline]
                        fn write(&mut self) -> Option<::interface::Data<#ident #ty_generics>> {
                            <Self as ::interface::Write<#name>>::write(self).map(|data| data.transmute())
                        }
                    }
                };
            }
            if a_trait.to_string().as_str().to_lowercase() == "read" {
                read = quote! {
                    impl #impl_generics ::interface::Read<#ident #ty_generics> for #client {
                        #[inline]
                        fn read(&mut self,data: ::interface::Data<#ident #ty_generics>) {
                            <Self as ::interface::Read<#name>>::read(self,data.transmute());
                        }
                    }
                };
            }
            if a_trait.to_string().as_str().to_lowercase() == "size" {
                size = quote! {
                    impl #impl_generics ::interface::Size<#ident #ty_generics> for #client {
                        #[inline]
                        fn len(&self) -> usize {
                            <Self as ::interface::Size<#name>>::len(self)
                        }
                    }
                };
            }
        }
        quote! {
            #uid
            #write
            #read
            #size
        }
    }
}