ownable-macro 1.0.0

Procedural macros for the ownable crate
Documentation
use crate::attribute::{FieldAttribute, OrAssign};
use crate::derive::Derive;
use darling::FromAttributes;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{FieldsNamed, FieldsUnnamed, LitInt, Type, TypeReference, Variant};

impl Derive<'_> {
    pub(crate) fn derive_named(
        &mut self,
        variant: Option<Variant>,
        data: &FieldsNamed,
    ) -> TokenStream {
        let mut fields = Vec::new();
        for d in &data.named {
            let mut field_attribute = self.handle(FieldAttribute::from_attributes(&d.attrs));
            if let Some(ref variant) = variant {
                field_attribute
                    .or_assign(&self.handle(FieldAttribute::from_attributes(&variant.attrs)));
            }
            field_attribute.or_assign(self.attribute);
            let name = d.ident.as_ref().unwrap();
            let call = self.create_call(
                &field_attribute,
                &name.into_token_stream(),
                variant.is_none(),
                &d.ty,
            );
            fields.push(quote! {#name: #call});
        }

        let name = self.ident;
        if let Some(variant) = variant {
            let variant_name = &variant.ident;
            quote! {#name :: #variant_name { #(#fields),* }}
        } else {
            quote! {#name { #(#fields),* }}
        }
    }

    pub(crate) fn derive_unnamed(
        &mut self,
        variant: Option<&Variant>,
        data: &FieldsUnnamed,
    ) -> TokenStream {
        let mut fields = Vec::new();
        for (i, d) in data.unnamed.iter().enumerate() {
            let mut field_attribute = self.handle(FieldAttribute::from_attributes(&d.attrs));
            if let Some(variant) = variant {
                field_attribute
                    .or_assign(&self.handle(FieldAttribute::from_attributes(&variant.attrs)));
                field_attribute.or_assign(self.attribute);
                fields.push(self.create_call(
                    &field_attribute,
                    &Ident::new(&format!("arg{i}"), Span::call_site()).to_token_stream(),
                    false,
                    &d.ty,
                ));
            } else {
                field_attribute.or_assign(self.attribute);
                fields.push(self.create_call(
                    &field_attribute,
                    &LitInt::new(&i.to_string(), Span::call_site()).to_token_stream(),
                    true,
                    &d.ty,
                ));
            }
        }

        let name = self.ident;
        if let Some(variant) = variant {
            let variant_name = &variant.ident;
            quote! {#name :: #variant_name ( #(#fields),* )}
        } else {
            quote! {#name ( #(#fields),* )}
        }
    }

    fn create_call(
        &mut self,
        field_attribute: &FieldAttribute,
        index: &TokenStream,
        with_self: bool,
        ty: &Type,
    ) -> TokenStream {
        if let Type::Reference(TypeReference {
            lifetime: Some(l), ..
        }) = ty
        {
            if self.attribute.is_reference_lifetime(&l.ident) {
                if with_self {
                    quote! { self . #index}
                } else {
                    quote! { #index }
                }
            } else {
                self.error(ty,"References are not supported out of the box, see: https://docs.rs/ownable/*/ownable/#references");
                quote! { todo!() }
            }
        } else if field_attribute.clone.unwrap_or(false) {
            if with_self {
                quote! {::core::clone::Clone::clone(& self . #index)}
            } else {
                quote! {::core::clone::Clone::clone(& #index)}
            }
        } else {
            let trait_name = self.mode.name();
            let trait_function = self.mode.function();
            if with_self {
                let as_ref = self.mode.as_ref();
                quote! {#trait_name::#trait_function(#as_ref self . #index)}
            } else {
                quote! {#trait_name::#trait_function(#index)}
            }
        }
    }
}