use std::borrow::Cow;
use std::slice;
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::spanned::Spanned as _;
use syn::{Token, Type};
use super::EmitContext;
use crate::model::*;
use crate::util::*;
pub fn emit_fields(ctx: &EmitContext<'_>) -> TokenStream {
let EmitContext {
builder,
impl_generics,
ty_generics,
where_clause,
fields,
..
} = ctx;
let mut output = TokenStream::new();
let t_true = simple_ident("true");
let t_false = simple_ident("false");
let unsafe_token = <Token![unsafe]>::default();
for (
index,
FieldInfo {
ident,
name,
ty,
vis,
doc,
deprecated,
setter,
..
},
) in fields.pub_api().enumerate()
{
let used_gens = fields
.pub_api()
.enumerate()
.map(|(i, f)| (i != index).then_some(&f.gen_name));
let pre_set_args = used_gens.clone().map(|o| o.unwrap_or(&t_false));
let post_set_args = used_gens.clone().map(|o| o.unwrap_or(&t_true));
let set_params = used_gens.flatten();
let allow_deprecated = allow_deprecated(*deprecated);
let mut ty = *ty;
let SplitSetter {
inputs,
cast,
tys,
life,
} = split_setter(setter, &mut ty);
output.extend(quote::quote_spanned! {ident.span()=>
impl < #impl_generics #( const #set_params: ::core::primitive::bool ),* >
#builder < #ty_generics #(#pre_set_args),* >
#where_clause
{
#(#doc)*
#deprecated
#[inline]
#[allow(clippy::type_repetition_in_bounds, clippy::multiple_bound_locations)]
#vis const fn #name #life (self, #inputs) -> #builder < #ty_generics #(#post_set_args),* >
where
#(#tys: ::core::marker::Sized,)*
{
#cast
#allow_deprecated
#unsafe_token { self.into_unchecked().#name(value).assert_init() }
}
}
});
}
output
}
fn split_setter<'t>(setter: &'t FieldSetter, ty: &'t mut &'t Type) -> SplitSetter<'t> {
match setter {
FieldSetter::Default => SplitSetter::simple(ty, None),
FieldSetter::StripOption => {
*ty = first_generic_arg(ty).unwrap_or(ty);
let cast = quote::quote! { let value = ::core::option::Option::Some(value); };
SplitSetter::simple(ty, Some(cast))
},
FieldSetter::Transform(transform) => SplitSetter::transform(transform),
}
}
struct SplitSetter<'t> {
inputs: SetterInputs<'t>,
cast: Option<TokenStream>,
tys: Cow<'t, [&'t Type]>,
life: Option<&'t AngleBracketedGenerics>,
}
impl<'t> SplitSetter<'t> {
fn simple(ty: &'t &'t Type, cast: Option<TokenStream>) -> Self {
Self {
inputs: SetterInputs::Value(ty),
cast,
tys: slice::from_ref(ty).into(),
life: None,
}
}
fn transform(transform: &'t FieldTransform) -> Self {
let body = &*transform.body;
let inputs = transform.inputs.pairs();
Self {
inputs: SetterInputs::Transform(transform),
cast: Some(quote::quote! { let value = #body; }),
tys: inputs.map(|t| &*t.into_value().ty).collect(),
life: transform.lifetimes.as_ref(),
}
}
}
enum SetterInputs<'a> {
Value(&'a Type),
Transform(&'a FieldTransform),
}
impl ToTokens for SetterInputs<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
match *self {
SetterInputs::Value(ty) => {
simple_ident("value").to_tokens(tokens);
<Token![:]>::default().to_tokens(tokens);
ty.to_tokens(tokens);
},
SetterInputs::Transform(inputs) => inputs.inputs.to_tokens(tokens),
}
}
}