bk2d-macro 0.3.0

Useful macro for impl BK2D
Documentation
use darling::FromDeriveInput;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{punctuated::Punctuated, DeriveInput, Ident, Path, Token, Type, TypePath};

#[derive(Default, FromDeriveInput)]
#[darling(attributes(player_fields), default)]
struct FieldsAttr {
    #[darling(rename = "ident")]
    ident_: Option<Ident>,
    location: Option<Ident>,
    ident_type: Option<Path>,
    #[darling(default)]
    no_display: bool,
}

pub fn derive(input: DeriveInput) -> TokenStream {
    let attr = FieldsAttr::from_derive_input(&input).expect("failed to parse attributes");
    let ident = attr
        .ident_
        .unwrap_or_else(|| Ident::new("ident", Span::call_site()));
    let location = attr
        .location
        .unwrap_or_else(|| Ident::new("location", Span::call_site()));
    let ident_path = attr.ident_type.unwrap_or_else(|| {
        use syn::{PathArguments::None as PN, PathSegment as PS};
        let mut path = Punctuated::new();
        path.push(PS {
            ident: Ident::new("std", Span::call_site()),
            arguments: PN,
        });
        path.push(PS {
            ident: Ident::new("string", Span::call_site()),
            arguments: PN,
        });
        path.push(PS {
            ident: Ident::new("String", Span::call_site()),
            arguments: PN,
        });
        Path {
            leading_colon: Some(Token![::](Span::call_site())),
            segments: path,
        }
    });
    let ident_type = Type::Path(TypePath {
        qself: None,
        path: ident_path,
    });
    let name = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let mut result = quote! {
        impl#impl_generics ::bk2d::Player for #name#ty_generics #where_clause {
            type Ident = #ident_type;

            fn ident(&self) -> &Self::Ident {
                &self.#ident
            }

            fn location(&self) -> &Point {
                &self.#location
            }
        }
    };
    if !attr.no_display {
        result.extend(
            (quote! {
                impl ::std::fmt::Display for Player {
                    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> fmt::Result {
                        let Point { x, y } = self.location();
                        write!(f, "{}({}, {})", self.ident(), x, y)
                    }
                }
            })
            .into_iter(),
        )
    };
    result
}