ircv3_parse_derive 4.0.0

Derive macro for ircv3_parse
Documentation
use proc_macro2::Span;
use syn::{
    Data, DataEnum, DataStruct, DeriveInput, Error, GenericParam, Ident, ImplGenerics, Lifetime,
    LifetimeParam, Result, TypeGenerics, WhereClause,
};

use crate::{
    attr::{EnumAttrs, FieldAttrs, StructAttrs, UnitStructAttrs, VariantAttrs},
    component_set::ComponentSet,
};

pub enum Input<'a> {
    Struct(Struct<'a>),
    Enum(Enum<'a>),
}

pub enum Struct<'a> {
    Unit(UnitStruct<'a>),
    Fields(FieldStruct<'a>),
}

pub struct Enum<'a> {
    pub ident: &'a Ident,
    pub generics: Generics<'a>,
    pub attrs: EnumAttrs,
    pub variants: Vec<Variant<'a>>,
}

pub struct UnitStruct<'a> {
    pub ident: &'a Ident,
    pub generics: Generics<'a>,
    pub attrs: UnitStructAttrs,
}

pub struct FieldStruct<'a> {
    pub ident: &'a Ident,
    pub generics: Generics<'a>,
    pub attrs: StructAttrs,
    pub fields: Vec<Field<'a>>,
}

pub struct Variant<'a> {
    pub ident: &'a Ident,
    pub attrs: VariantAttrs,
    pub fields: VariantFields<'a>,
}

pub enum VariantFields<'a> {
    Unit,
    Named(Vec<Field<'a>>),
    Unnamed(Vec<Field<'a>>),
}

pub struct Field<'a> {
    pub attrs: FieldAttrs,
    pub field: &'a syn::Field,
}

pub struct Generics<'a> {
    original: &'a syn::Generics,
    generics: syn::Generics,
}

impl<'a> Input<'a> {
    pub fn from_syn(input: &'a DeriveInput, name: &str) -> Result<Self> {
        match &input.data {
            Data::Struct(data_struct) => Struct::from_syn(input, data_struct).map(Input::Struct),
            Data::Enum(data_enum) => Enum::from_syn(input, data_enum).map(Input::Enum),
            _ => Err(Error::new_spanned(
                &input.ident,
                format!("{name} is not supported for unions"),
            )),
        }
    }
}

impl<'a> Struct<'a> {
    pub fn from_syn(input: &'a DeriveInput, data_struct: &'a DataStruct) -> Result<Self> {
        let ident = &input.ident;
        let generics = Generics::new(&input.generics);

        match &data_struct.fields {
            syn::Fields::Unit => {
                let attrs = UnitStructAttrs::parse(&input.attrs)?;

                Ok(Struct::Unit(UnitStruct {
                    ident,
                    generics,
                    attrs,
                }))
            }
            syn::Fields::Unnamed(fields) => {
                let attrs = StructAttrs::parse(&input.attrs)?;
                let fields = fields
                    .unnamed
                    .iter()
                    .map(Field::parse)
                    .collect::<Result<Vec<_>>>()?;

                Ok(Struct::Fields(FieldStruct {
                    ident,
                    generics,
                    attrs,
                    fields,
                }))
            }
            syn::Fields::Named(fields) => {
                let attrs = StructAttrs::parse(&input.attrs)?;
                let fields = fields
                    .named
                    .iter()
                    .map(Field::parse)
                    .collect::<Result<Vec<_>>>()?;

                Ok(Struct::Fields(FieldStruct {
                    ident,
                    generics,
                    attrs,
                    fields,
                }))
            }
        }
    }
}

impl<'a> Enum<'a> {
    pub fn from_syn(input: &'a DeriveInput, data_enum: &'a DataEnum) -> Result<Self> {
        let ident = &input.ident;

        let attrs = EnumAttrs::parse(&input.attrs, ident)?;

        let variants = data_enum
            .variants
            .iter()
            .map(Variant::parse)
            .collect::<Result<Vec<_>>>()?;

        Ok(Self {
            ident,
            generics: Generics::new(&input.generics),
            attrs,
            variants,
        })
    }
}

impl<'a> FieldStruct<'a> {
    pub fn components(&self) -> ComponentSet {
        let mut components = ComponentSet::default();

        for field in &self.fields {
            field.add_to(&mut components);
        }

        components
    }
}

impl<'a> Variant<'a> {
    pub fn parse(variant: &'a syn::Variant) -> Result<Self> {
        let attrs = VariantAttrs::parse(&variant.attrs)?;
        let fields = VariantFields::parse(&variant.fields)?;

        Ok(Self {
            ident: &variant.ident,
            attrs,
            fields,
        })
    }
}

impl<'a> VariantFields<'a> {
    pub fn parse(fields: &'a syn::Fields) -> Result<Self> {
        match fields {
            syn::Fields::Unit => Ok(Self::Unit),
            syn::Fields::Named(fields) => fields
                .named
                .iter()
                .map(Field::parse)
                .collect::<Result<Vec<_>>>()
                .map(Self::Named),
            syn::Fields::Unnamed(fields) => fields
                .unnamed
                .iter()
                .map(Field::parse)
                .collect::<Result<Vec<_>>>()
                .map(Self::Unnamed),
        }
    }
}

impl<'a> Field<'a> {
    pub fn parse(field: &'a syn::Field) -> Result<Self> {
        FieldAttrs::parse(field).map(|attrs| Self { attrs, field })
    }

    pub fn add_to(&self, components: &mut ComponentSet) {
        self.attrs.add_to(components);
    }
}

impl<'a> Generics<'a> {
    pub fn new(generics: &'a syn::Generics) -> Self {
        let mut impl_generics = generics.clone();

        if impl_generics.lifetimes().next().is_none() {
            let lifetime = Lifetime::new("'_msg", Span::call_site());
            impl_generics
                .params
                .insert(0, GenericParam::Lifetime(LifetimeParam::new(lifetime)));
        }

        Self {
            generics: impl_generics,
            original: generics,
        }
    }

    pub fn split(&self) -> (ImplGenerics<'_>, TypeGenerics<'_>, Option<&WhereClause>) {
        let (impl_generics, _, where_clause) = self.generics.split_for_impl();
        let (_, struct_ty_generics, _) = self.original.split_for_impl();

        (impl_generics, struct_ty_generics, where_clause)
    }

    pub fn msg_lifetime(&self) -> Lifetime {
        self.generics
            .lifetimes()
            .next()
            .map(|param| param.lifetime.clone())
            .expect("msg_lifetime should exist after new()")
    }
}