use std::collections::HashMap;
use proc_macro2::{TokenStream};
use syn::{ItemStruct, Ident, Result, Token, punctuated::Punctuated, Type};
use syn::parse::{Parse, ParseStream};
use quote::quote;
use super::{Generate, ParentAttribute, RoopertAttribute, RoopertAttributeType};
use super::parse::{is_parent_attribute, is_roopert_attribute};
#[cfg_attr(feature="verbose", derive(Debug))]
pub struct ExtendsAttribute {
types: Punctuated<Type, Token![,]>,
}
impl ExtendsAttribute {
fn impl_asref(target_struct: &ItemStruct, target_field: &Ident, parent_type: &Type) -> TokenStream {
let target_struct_ident = &target_struct.ident;
let (impl_generics, ty_generics, where_clause) = target_struct.generics.split_for_impl();
quote!{
impl #impl_generics core::convert::AsRef<#parent_type> for #target_struct_ident #ty_generics #where_clause{
fn as_ref(&self) -> &#parent_type {
&self.#target_field
}
}
}
}
fn impl_asmut(target_struct: &ItemStruct, target_field: &Ident, parent_type: &Type) -> TokenStream {
let target_struct_ident = &target_struct.ident;
let (impl_generics, ty_generics, where_clause) = target_struct.generics.split_for_impl();
quote!{
impl #impl_generics core::convert::AsMut<#parent_type> for #target_struct_ident #ty_generics #where_clause {
fn as_mut(&mut self) -> &mut #parent_type {
&mut self.#target_field
}
}
}
}
fn impl_into(target_struct: &ItemStruct, target_field: &Ident, parent_type: &Type) -> TokenStream {
let target_struct_ident = &target_struct.ident;
let (impl_generics, ty_generics, where_clause) = target_struct.generics.split_for_impl();
quote!{
impl #impl_generics core::convert::Into<#parent_type> for #target_struct_ident #ty_generics #where_clause {
fn into(self) -> #parent_type {
self.#target_field
}
}
}
}
fn impl_deref(target_struct: &ItemStruct, target_field: &Ident, parent_type: &Type) -> TokenStream {
let target_struct_ident = &target_struct.ident;
let (impl_generics, ty_generics, where_clause) = target_struct.generics.split_for_impl();
quote!{
impl #impl_generics core::ops::Deref for #target_struct_ident #ty_generics #where_clause {
type Target = #parent_type;
fn deref(&self) -> &Self::Target {
&self.#target_field
}
}
}
}
fn impl_derefmut(target_struct: &ItemStruct, target_field: &Ident, _parent_type: &Type) -> TokenStream {
let target_struct_ident = &target_struct.ident;
let (impl_generics, ty_generics, where_clause) = target_struct.generics.split_for_impl();
quote!{
impl #impl_generics core::ops::DerefMut for #target_struct_ident #ty_generics #where_clause {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.#target_field
}
}
}
}
}
impl Parse for ExtendsAttribute {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Self{
types: Punctuated::<Type, Token![,]>::parse_separated_nonempty(input)?,
})
}
}
impl Generate for ExtendsAttribute {
fn generate(&mut self, input: TokenStream) -> core::result::Result<TokenStream, String> {
let mut target_struct: ItemStruct = syn::parse(input.into()).map_err(|_| "Only named structs objects can be extended".to_string())?;
let mut type_map = HashMap::<Type, Ident>::new();
for field in target_struct.fields.iter_mut() {
let mut new_attributes = Vec::with_capacity(field.attrs.len());
for attr in &field.attrs {
let is_parent_path = is_parent_attribute(attr);
let is_roopert_path = is_roopert_attribute(attr);
let mut parent_attr: Option<ParentAttribute> = None;
if is_parent_path {
parent_attr = Some(attr.parse_args::<ParentAttribute>().map_err(|_| "Malformed roopert #[parent] attribute".to_string())?);
} else if is_roopert_path {
let parsed_attr = attr.parse_args::<RoopertAttribute>().map_err(|_| "Malformed #[roopert(parent)] attribute".to_string())?;
if !parsed_attr.attr.is_parent() {
new_attributes.push(attr.clone()); continue;
}
parent_attr = Some(match parsed_attr.attr {
RoopertAttributeType::Parent(a) => Ok(a),
_ => Err("Encountered quantum superpositioned #[roopert(???)] attribute (is_extends() -> true but not Extends)".to_string())
}?);
} else {
new_attributes.push(attr.clone()); }
if parent_attr.is_some() {
type_map.insert(field.ty.clone(), field.ident.clone().unwrap());
break;
}
}
field.attrs = new_attributes;
if !type_map.contains_key(&field.ty) {
type_map.insert(field.ty.clone(), field.ident.clone().unwrap());
}
}
let mut tokens = vec![quote!{#target_struct}];
for parent_type in self.types.iter() {
let target_field = match type_map.get(parent_type) {
Some(x) => Ok(x),
None => Err("Cannot extend type not which is not also a field of this struct".to_string())
}?;
let token = Self::impl_asref(&target_struct, target_field, parent_type);
tokens.push(token);
let token = Self::impl_asmut(&target_struct, target_field, parent_type);
tokens.push(token);
let token = Self::impl_into(&target_struct, target_field, parent_type);
tokens.push(token);
let token = Self::impl_deref(&target_struct, target_field, parent_type);
tokens.push(token);
let token = Self::impl_derefmut(&target_struct, target_field, parent_type);
tokens.push(token);
}
Ok(quote!{
#(#tokens)*
})
}
fn auto_append(&self) -> bool {false}
}