shipyard_proc 0.6.1

Crate providing attribute macro to Shipyard
Documentation
use proc_macro2::{Span, TokenStream, TokenTree};
use quote::quote;
use syn::{Error, Result};

pub(crate) fn expand_component(
    name: syn::Ident,
    generics: syn::Generics,
    attribute_input: Option<&syn::Attribute>,
) -> Result<TokenStream> {
    let tracking = if let Some(tracking_attr) = attribute_input {
        let mut track_insertion = false;
        let mut track_modification = false;
        let mut track_deletion = false;
        let mut track_removal = false;

        match &tracking_attr.meta {
            syn::Meta::List(list) => {
                for token in list.tokens.clone() {
                    if let TokenTree::Ident(ident) = token {
                        if ident == "Insertion" {
                            track_insertion = true;
                        } else if ident == "Modification" {
                            track_modification = true;
                        } else if ident == "Deletion" {
                            track_deletion = true;
                        } else if ident == "Removal" {
                            track_removal = true;
                        } else if ident == "All" {
                            track_insertion = true;
                            track_modification = true;
                            track_deletion = true;
                            track_removal = true;
                        } else {
                            return Err(Error::new_spanned(
                                &ident,
                                "Track should be either: Insertion, Modification, Deletion, Removal or All.",
                            ))
                        }
                    }
                }
            },
            _ => {
                return Err(Error::new_spanned(
                    &tracking_attr.meta,
                    "Track should be a list of either: Insertion, Modification, Deletion, Removal or All.",
                ))
            }
        };

        let tracking = match (
            track_insertion,
            track_modification,
            track_deletion,
            track_removal,
        ) {
            (true, true, true, true) => "All",
            (true, true, true, false) => "InsertionAndModificationAndDeletion",
            (true, true, false, true) => "InsertionAndModificationAndRemoval",
            (true, true, false, false) => "InsertionAndModification",
            (true, false, true, true) => "InsertionAndDeletionAndRemoval",
            (true, false, true, false) => "InsertionAndDeletion",
            (true, false, false, true) => "InsertionAndRemoval",
            (true, false, false, false) => "Insertion",
            (false, true, true, true) => "ModificationAndDeletionAndRemoval",
            (false, true, true, false) => "ModificationAndDeletion",
            (false, true, false, true) => "ModificationAndRemoval",
            (false, true, false, false) => "Modification",
            (false, false, true, true) => "DeletionAndRemoval",
            (false, false, true, false) => "Deletion",
            (false, false, false, true) => "Removal",
            (false, false, false, false) => "Untracked",
        };

        syn::Ident::new(tracking, Span::call_site())
    } else {
        syn::Ident::new("Untracked", Span::call_site())
    };

    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    Ok(quote!(
        impl #impl_generics ::shipyard::Component for #name #ty_generics #where_clause {
            type Tracking = ::shipyard::track::#tracking;
        }
    ))
}

pub(crate) fn expand_unique(name: syn::Ident, generics: syn::Generics) -> TokenStream {
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    quote!(
        impl #impl_generics ::shipyard::Unique for #name #ty_generics #where_clause {}
    )
}