mapstruct-derive-lib 0.4.0

A derive macro for mapstruct
Documentation
use quote::ToTokens;
use syn::parse::{Parse, ParseStream};
use crate::transformer::Transformer;
use crate::unnamed_field_change::UnnamedFieldChange;

pub struct TupleChange {
    changes: Vec<UnnamedFieldChange>,
}

impl Parse for TupleChange {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let content;
        syn::parenthesized!(content in input);
        let changes = content.parse_terminated(
            UnnamedFieldChange::parse,
            syn::Token![,]
        )?;
        let changes = changes.into_iter().collect();
        Ok(TupleChange { changes })
    }
}

impl Transformer for TupleChange {
    type Item = syn::FieldsUnnamed;
    type CreateIter = std::option::IntoIter<Self::Item>;

    fn create(&self) -> syn::Result<Self::CreateIter> {
        Ok(None.into_iter())
    }

    fn transform(&self, item: &mut Self::Item) -> syn::Result<bool> {
        let mut new_fields = Vec::new();
        let mut old_fields = item.unnamed.iter();
        let mut changes = self.changes.iter();
        while let Some(change) = changes.next() {
            match change {
                UnnamedFieldChange::Add { visibility, ty } => {
                    new_fields.push(syn::Field {
                        attrs: Vec::new(),
                        vis: visibility.clone(),
                        ty: ty.clone(),
                        ident: None,
                        mutability: syn::FieldMutability::None,
                        colon_token: None,
                    });
                }
                UnnamedFieldChange::Remove { ty } => {
                    if let Some(ty) = ty {
                        if let Some(field) = old_fields.next() {
                            if field.ty.to_token_stream().to_string() != ty.to_token_stream().to_string() {
                                return Err(syn::Error::new_spanned(
                                    field,
                                    "Expected field to be removed but type did not match",
                                ));
                            } else {
                                continue;
                            }
                        }
                    }

                    return Err(syn::Error::new_spanned(
                        item,
                        "Expected field to be removed but there are no more fields",
                    ));
                }
                UnnamedFieldChange::Retype { old_type, new_type } => {
                    if let Some(old_type) = old_type {
                        if let Some(field) = old_fields.next() {
                            if field.ty.to_token_stream().to_string() != old_type.to_token_stream().to_string() {
                                return Err(syn::Error::new_spanned(
                                    field,
                                    "Expected field to be retyped but original type did not match",
                                ));
                            } else {
                                new_fields.push(syn::Field {
                                    ty: new_type.clone(),
                                    ..field.clone()
                                });
                                continue;
                            }
                        }
                    } else {
                        if let Some(field) = old_fields.next() {
                            new_fields.push(syn::Field {
                                ty: new_type.clone(),
                                ..field.clone()
                            });
                            continue;
                        }
                    }

                    return Err(syn::Error::new_spanned(
                        item,
                        "Expected field to be retyped but there are no more fields",
                    ));
                }
                UnnamedFieldChange::Match { ty } => {
                    if let Some(field) = old_fields.next() {
                        if let Some(ty) = ty {
                            if field.ty.to_token_stream().to_string() != ty.to_token_stream().to_string() {
                                return Err(syn::Error::new_spanned(
                                    field,
                                    "Expected field to match but type did not match",
                                ));
                            } else {
                                new_fields.push(field.clone());
                                continue;
                            }
                        } else {
                            new_fields.push(field.clone());
                            continue;
                        }
                    }

                    return Err(syn::Error::new_spanned(
                        item,
                        "Expected field to match but there are no more fields",
                    ));
                }
            }
        }

        if let Some(field) = old_fields.next() {
            return Err(syn::Error::new_spanned(
                field,
                "Expected no more fields but there are more fields",
            ));
        }

        item.unnamed = syn::punctuated::Punctuated::from_iter(new_fields);

        Ok(true)
    }

    fn remove(&self, _: &Self::Item) -> syn::Result<bool> {
        Ok(false)
    }
}