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)),
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
}