use crate::common;
use crate::common::TargetTrait;
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{Field, FieldsNamed, FieldsUnnamed, Generics};
pub(super) fn generate_struct_named(
name: &Ident,
generics: &Generics,
fields_named: &FieldsNamed,
) -> TokenStream {
fields_named.named.iter().for_each(common::check_field);
let generic_idents = super::common::generic_idents(generics);
let to = generate_struct_named_to(name, generics, &generic_idents, fields_named);
let into = generate_struct_named_into(name, generics, &generic_idents, fields_named);
quote!(#to #into)
}
pub(super) fn generate_struct_unnamed(
name: &Ident,
generics: &Generics,
fields_unnamed: &FieldsUnnamed,
) -> TokenStream {
fields_unnamed.unnamed.iter().for_each(common::check_field);
let generic_idents = super::common::generic_idents(generics);
let to = generate_struct_unnamed_to(name, generics, &generic_idents, fields_unnamed);
let into = generate_struct_unnamed_into(name, generics, &generic_idents, fields_unnamed);
quote!(#to #into)
}
pub(super) fn generate_struct_unit(name: &Ident) -> TokenStream {
let to = generate_struct_unit_to(name);
let into = generate_struct_unit_into(name);
quote!(#to #into)
}
fn generate_struct_named_to(
name: &Ident,
generics: &Generics,
generic_idents: &[&Ident],
fields_named: &FieldsNamed,
) -> TokenStream {
let fields =
make_named_fields_init_methods(fields_named, generic_idents, TargetTrait::ToBoundedStatic);
let gens = common::make_bounded_generics(generics, TargetTrait::ToBoundedStatic);
let (impl_gens, ty_gens, where_clause) = gens.split_for_impl();
let static_gens = common::make_target_generics(generics, TargetTrait::ToBoundedStatic);
quote!(
impl #impl_gens ::bounded_static::ToBoundedStatic for #name #ty_gens #where_clause {
type Static = #name<#(#static_gens),*>;
fn to_static(&self) -> Self::Static {
#name {
#(#fields),*
}
}
}
)
}
fn generate_struct_named_into(
name: &Ident,
generics: &Generics,
generic_idents: &[&Ident],
fields_named: &FieldsNamed,
) -> TokenStream {
let fields = make_named_fields_init_methods(
fields_named,
generic_idents,
TargetTrait::IntoBoundedStatic,
);
let gens = common::make_bounded_generics(generics, TargetTrait::IntoBoundedStatic);
let (impl_gens, ty_gens, where_clause) = gens.split_for_impl();
let static_gens = common::make_target_generics(generics, TargetTrait::IntoBoundedStatic);
quote!(
impl #impl_gens ::bounded_static::IntoBoundedStatic for #name #ty_gens #where_clause {
type Static = #name<#(#static_gens),*>;
fn into_static(self) -> Self::Static {
#name {
#(#fields),*
}
}
}
)
}
fn generate_struct_unnamed_to(
name: &Ident,
generics: &Generics,
generic_idents: &[&Ident],
fields_unnamed: &FieldsUnnamed,
) -> TokenStream {
let fields = make_unnamed_fields(fields_unnamed, generic_idents, TargetTrait::ToBoundedStatic);
let gens = common::make_bounded_generics(generics, TargetTrait::ToBoundedStatic);
let (impl_gens, ty_gens, where_clause) = gens.split_for_impl();
let static_gens = common::make_target_generics(generics, TargetTrait::ToBoundedStatic);
quote!(
impl #impl_gens ::bounded_static::ToBoundedStatic for #name #ty_gens #where_clause {
type Static = #name<#(#static_gens),*>;
fn to_static(&self) -> Self::Static {
#name (
#(#fields),*
)
}
}
)
}
fn generate_struct_unnamed_into(
name: &Ident,
generics: &Generics,
generic_idents: &[&Ident],
fields_unnamed: &FieldsUnnamed,
) -> TokenStream {
let fields = make_unnamed_fields(
fields_unnamed,
generic_idents,
TargetTrait::IntoBoundedStatic,
);
let gens = common::make_bounded_generics(generics, TargetTrait::IntoBoundedStatic);
let (impl_gens, ty_gens, where_clause) = gens.split_for_impl();
let static_gens = common::make_target_generics(generics, TargetTrait::IntoBoundedStatic);
quote!(
impl #impl_gens ::bounded_static::IntoBoundedStatic for #name #ty_gens #where_clause {
type Static = #name<#(#static_gens),*>;
fn into_static(self) -> Self::Static {
#name (
#(#fields),*
)
}
}
)
}
fn generate_struct_unit_to(name: &Ident) -> TokenStream {
quote!(
impl ::bounded_static::ToBoundedStatic for #name {
type Static = #name;
fn to_static(&self) -> Self::Static {
#name
}
}
)
}
fn generate_struct_unit_into(name: &Ident) -> TokenStream {
quote!(
impl ::bounded_static::IntoBoundedStatic for #name {
type Static = #name;
fn into_static(self) -> Self::Static {
#name
}
}
)
}
fn make_named_fields_init_methods(
fields_named: &FieldsNamed,
generic_idents: &[&Ident],
target: TargetTrait,
) -> Vec<TokenStream> {
fields_named
.named
.iter()
.map(|field| make_named_field_init_method(field, generic_idents, target))
.collect()
}
fn make_named_field_init_method(
field: &Field,
generic_idents: &[&Ident],
target: TargetTrait,
) -> TokenStream {
let field_name = field
.ident
.as_ref()
.expect("FieldsNamed field must have an ident");
if super::common::has_non_static_lifetime(&field.ty, generic_idents) {
let method = target.method();
quote!(#field_name: self.#field_name.#method())
} else if target.needs_clone() {
quote!(#field_name: self.#field_name.clone())
} else {
quote!(#field_name: self.#field_name)
}
}
fn make_unnamed_fields(
fields_unnamed: &FieldsUnnamed,
generic_idents: &[&Ident],
target: TargetTrait,
) -> Vec<TokenStream> {
let fields_to_static: Vec<_> = fields_unnamed
.unnamed
.iter()
.enumerate()
.map(|(i, field)| make_unnamed_field(i, field, generic_idents, target))
.collect();
fields_to_static
}
fn make_unnamed_field(
i: usize,
field: &Field,
generic_idents: &[&Ident],
target: TargetTrait,
) -> TokenStream {
let i = syn::Index::from(i);
if super::common::has_non_static_lifetime(&field.ty, generic_idents) {
let method = target.method();
quote!(self.#i.#method())
} else if target.needs_clone() {
quote!(self.#i.clone())
} else {
quote!(self.#i)
}
}