use crate::buildstructor::utils::{
AngleBracketedGenericArgumentsExt, ExprTupleExt, GenericsExt, IdentExt, TypeExt, TypeTupleExt,
};
use crate::lower::{FieldType, Ir};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote};
use syn::punctuated::Punctuated;
use syn::{
Expr, ExprCall, GenericArgument, GenericParam, Generics, Index, Lifetime, LifetimeParam,
Result, Token, Type, TypeReference, TypeTuple, WhereClause,
};
extern crate inflector;
use inflector::Inflector;
pub fn codegen(ir: Ir) -> Result<TokenStream> {
let module_name = &ir.module_name;
let target_name = &ir.impl_name;
let builder_alias_name = format_ident!(
"{}{}Builder",
ir.delegate_name.to_string().to_pascal_case(),
ir.impl_name,
);
let self_ty = &ir.self_ty;
let (impl_generics, ty_generics, where_clause) = &ir.impl_generics.split_for_impl();
let param_generics = ir.param_generics();
let builder_lifetime = ir
.receiver
.as_ref()
.and_then(|r| r.reference.as_ref())
.map(|_| Lifetime::new("'__builder", Span::call_site()));
let builder_lifetime_generics = Generics {
params: builder_lifetime
.as_ref()
.map(|l| {
Punctuated::from_iter(vec![GenericParam::Lifetime(LifetimeParam::new(l.clone()))])
})
.unwrap_or_default(),
..Default::default()
};
let builder_generics = Generics::combine(vec![
&builder_lifetime_generics,
&ir.impl_generics,
&ir.delegate_generics,
¶m_generics,
]);
let (builder_impl_generics, _, builder_where_clause) = builder_generics.split_for_impl();
let builder_generics_tuple = Generics::combine(vec![
&builder_lifetime_generics,
&ir.impl_generics,
&ir.delegate_generics,
]);
let builder_tuple_ty_generics = builder_generics_tuple
.to_generic_args()
.insert(0, Type::Tuple(param_generics.to_tuple_type()));
let all_generics = Generics::combine(vec![
&builder_lifetime_generics,
&ir.builder_generics,
&ir.impl_generics,
&ir.delegate_generics,
]);
let (_, all_ty_generics, _) = all_generics.split_for_impl();
let method_generics = &ir.delegate_generics;
let builder_init_generics = Generics::combine(vec![&ir.impl_generics, &ir.delegate_generics]);
let builder_init_generic_args = builder_init_generics.to_generic_args().maybe();
let builder_init_generic_args_with_lifetime = builder_init_generics
.to_generic_bounds()
.with_implicit_lifetime(ir.implicit_lifetime);
let builder_init_generic_args_phantom = builder_init_generic_args.clone().map(|a| {
let args: Punctuated<GenericArgument, Token![,]> =
Punctuated::from_iter(a.args.into_iter().map(|a| match a {
GenericArgument::Lifetime(l) => {
GenericArgument::Type(Type::Reference(TypeReference {
and_token: Default::default(),
lifetime: Some(l),
mutability: None,
elem: Box::new(Type::Tuple(TypeTuple {
paren_token: Default::default(),
elems: Default::default(),
})),
}))
}
_ => a,
}));
args
});
let builder_init_generic_args_with_state = builder_init_generics
.to_generic_args()
.insert(0, ir.builder_state_type_initial());
let builder_init_generic_args_with_state_with_lifetime = builder_init_generics
.to_generic_args()
.insert(0, ir.builder_state_type_initial())
.with_implicit_lifetime(ir.implicit_lifetime);
let delegate_name = &ir.delegate_name;
let delegate_args = ir.delegate_args();
let builder_name = &ir.builder_name;
let builder_return_type = &ir.builder_return_type;
let builder_entry = &ir.builder_entry;
let builder_exit = &ir.builder_exit;
let builder_state_initial = ir.builder_state_initial();
let async_token = ir.is_async.then(|| quote! {async});
let await_token = ir.is_async.then(|| quote! {.await});
let vis = &ir.vis;
let builder_vis = &ir.builder_vis;
let receiver = &ir.receiver;
let reference = &ir
.receiver
.as_ref()
.map(|r| r.reference.as_ref().map(|_| quote! { & }));
let mutability = ir.receiver.as_ref().map(|r| r.mutability);
let builder_receiver = ir.receiver.as_ref().map(|_| quote! { self });
let builder_receiver_param = ir
.receiver
.as_ref()
.map(|_| quote! { receiver: #reference #mutability #target_name #ty_generics });
let builder_receiver_field_definition = ir
.receiver
.as_ref()
.map(|_| quote! { receiver: #reference #builder_lifetime #mutability #target_name #ty_generics, });
let builder_receiver_field = ir.receiver.as_ref().map(|_| quote! { receiver, });
let builder_receiver_call = ir
.receiver
.as_ref()
.map(|_| quote! { self.receiver. })
.unwrap_or_else(|| quote! {#target_name::});
let builder_receiver_move = ir
.receiver
.as_ref()
.map(|_| quote! { receiver: self.receiver, });
let builder_methods = builder_methods(
&ir,
builder_where_clause,
builder_receiver_move,
&builder_lifetime_generics,
)?;
let doc = ir.doc;
let type_doc = "Autogenerated by buildstructor";
Ok(quote! {
impl #impl_generics #self_ty #where_clause {
#(#doc)*
#[must_use]
#vis fn #builder_entry #method_generics(#receiver) -> #builder_alias_name #builder_init_generic_args {
#module_name::new(#builder_receiver)
}
}
#[doc=#type_doc]
#[allow(type_alias_bounds)]
#vis type #builder_alias_name #builder_init_generic_args_with_lifetime = #module_name::#builder_name #builder_init_generic_args_with_state_with_lifetime;
mod #module_name {
use super::*;
#[inline(always)]
#[must_use]
#builder_vis fn new #builder_init_generics(#builder_receiver_param) -> #builder_name #builder_init_generic_args_with_state
{
#builder_name {
#builder_receiver_field
fields: (#(#builder_state_initial ,) *),
_phantom: core::default::Default::default()
}
}
#builder_vis struct __Required<T> {
_uninit: std::mem::MaybeUninit<T>,
}
#builder_vis struct __Optional<T> {
lazy: Option<T>,
}
#builder_vis struct __Set<T> {
value: T,
}
#[inline(always)]
fn __set<T>(value: T) -> __Set<T> {
__Set { value }
}
#[inline(always)]
fn __required<T>() -> __Required<T> {
__Required::<T> {
_uninit: std::mem::MaybeUninit::uninit(),
}
}
#[inline(always)]
fn __optional<T>() -> __Optional<T> {
__Optional::<T> {
lazy: None,
}
}
impl<T: Default> From<__Optional<T>> for __Set<T> {
#[inline(always)]
fn from(o: __Optional<T>) -> Self {
__Set {
value: o.lazy.unwrap_or_default(),
}
}
}
#builder_vis struct #builder_name #all_ty_generics {
#builder_receiver_field_definition
fields: __P,
_phantom: core::marker::PhantomData<(#builder_init_generic_args_phantom)>
}
#(#builder_methods)*
impl #builder_impl_generics #builder_name #builder_tuple_ty_generics #builder_where_clause {
#[inline(always)]
#builder_vis #async_token fn #builder_exit(self) #builder_return_type {
#builder_receiver_call #delegate_name(#(#delegate_args),*) #await_token
}
}
}
})
}
pub fn builder_methods(
ir: &Ir,
builder_where_clause: Option<&WhereClause>,
builder_receiver_move: Option<TokenStream>,
builder_lifetime_generics: &Generics,
) -> Result<Vec<TokenStream>> {
let builder_generics = Generics::combine(vec![
builder_lifetime_generics,
&ir.impl_generics,
&ir.delegate_generics,
]);
let builder_vis = &ir.builder_vis;
Ok(ir.builder_fields
.iter()
.enumerate()
.map(|(idx, f)| {
let builder_name = &ir.builder_name;
let field_name = &f.name;
let method_name = format_ident!("{}", f.name);
let ty = &f.ty;
let builder_type_generics = ir.builder_type_generics();
let params_before = match f.field_type {
FieldType::Regular =>
builder_type_generics
.to_tuple_type()
.with_type(idx, f.ty.clone().wrap_in_generic(format_ident!("__Required"))),
_ => builder_type_generics
.to_tuple_type()
.with_type(idx, f.ty.clone().wrap_in_generic(format_ident!("__Optional"))),
};
let params_after = match f.field_type {
FieldType::Regular | FieldType::Option =>
builder_type_generics
.to_tuple_type()
.with_type(idx, f.ty.clone().wrap_in_generic(format_ident!("__Set"))),
_ => builder_type_generics
.to_tuple_type()
.with_type(idx, f.ty.clone().wrap_in_generic(format_ident!("__Optional"))),
};
let before = builder_generics
.to_generic_args()
.insert(0, Type::Tuple(params_before));
let after = builder_generics
.to_generic_args()
.insert(0, Type::Tuple(params_after));
let set = call(format_ident!("__set"), vec![Expr::Path(field_name.to_expr_path())]);
let new_state = params(ir, idx, field_name, &builder_type_generics, set);
let builder_type_generics = Generics::combine(vec![&builder_type_generics.without(idx), &builder_generics]);
match f.field_type {
FieldType::Option => {
let and_method_name = format_ident!("and_{}", f.name);
let mut field_collection_type = f.generic_types.generic_type.clone();
let mut into_generics = None;
let mut into_call = None;
if f.generic_types.generic_into {
let into_type = field_collection_type.replace(Type::parse("__T"));
let _ = into_generics.insert(Some(quote! {
<__T: Into<#into_type>>
}));
into_call = Some(quote!{
.into()
})
}
quote! {
impl #builder_type_generics #builder_name #before {
#[inline(always)]
#[must_use]
#builder_vis fn #method_name #into_generics(self, #field_name: #field_collection_type) -> #builder_name #after #builder_where_clause {
let #field_name = Some(#field_name #into_call);
#builder_name {
#builder_receiver_move
fields: #new_state,
_phantom: core::default::Default::default()
}
}
#[inline(always)]
#[must_use]
#builder_vis fn #and_method_name #into_generics(self, #field_name: Option<#field_collection_type>) -> #builder_name #after #builder_where_clause {
let #field_name = #field_name.map(|v|v #into_call);
#builder_name {
#builder_receiver_move
fields: #new_state,
_phantom: core::default::Default::default()
}
}
}
}
},
FieldType::Set => {
let (singular, plural) = single_plural_names(field_name);
let mut field_collection_type = f.generic_types.generic_type.clone();
let mut into_generics = None;
let mut into_call = None;
if f.generic_types.generic_into {
let into_type = field_collection_type.replace(Type::parse("__T"));
let _ = into_generics.insert(Some(quote! {
<__T: Into<#into_type>>
}));
into_call = Some(quote!{
.into()
})
}
let index = Index::from(idx);
quote! {
impl #builder_type_generics #builder_name #before {
#[inline(always)]
#[must_use]
#builder_vis fn #plural (mut self, #field_name: #ty) -> #builder_name #before #builder_where_clause {
self.fields.#index.lazy.get_or_insert_with(||core::default::Default::default()).extend(#field_name.into_iter());
self
}
#[inline(always)]
#[must_use]
#builder_vis fn #singular #into_generics(mut self, value: #field_collection_type) -> #builder_name #before #builder_where_clause{
self.fields.#index.lazy.get_or_insert_with(||core::default::Default::default()).insert(value #into_call);
self
}
}
}
},
FieldType::Vec => {
let (singular, plural) = single_plural_names(field_name);
let mut field_collection_type = f.generic_types.generic_type.clone();
let mut into_generics = None;
let mut into_call = None;
if f.generic_types.generic_into {
let into_type = field_collection_type.replace(Type::parse("__T"));
let _ = into_generics.insert(Some(quote! {
<__T: Into<#into_type>>
}));
into_call = Some(quote!{
.into()
})
}
let index = Index::from(idx);
quote! {
impl #builder_type_generics #builder_name #before {
#[inline(always)]
#[must_use]
#builder_vis fn #plural (mut self, #field_name: #ty) -> #builder_name #before #builder_where_clause{
self.fields.#index.lazy.get_or_insert_with(||core::default::Default::default()).extend(#field_name.into_iter());
self
}
#[inline(always)]
#[must_use]
#builder_vis fn #singular #into_generics(mut self, value: #field_collection_type) -> #builder_name #before #builder_where_clause{
self.fields.#index.lazy.get_or_insert_with(||core::default::Default::default()).push(value #into_call);
self
}
}
}
},
FieldType::Map => {
let (singular, plural) = single_plural_names(field_name);
let mut field_key_type = f.generic_types.key_type.clone();
let mut field_value_type = f.generic_types.value_type.clone();
let mut into_generics = Vec::new();
let mut field_key_into_call = None;
let mut field_value_into_call = None;
if f.generic_types.key_into {
let into_type = field_key_type.replace(Type::parse("__K"));
into_generics.push(quote! {
__K: Into<#into_type>
});
field_key_into_call = Some(quote!{
.into()
})
}
if f.generic_types.value_into {
let into_type = field_value_type.replace(Type::parse("__V"));
into_generics.push(quote! {
__V: Into<#into_type>
});
field_value_into_call = Some(quote!{
.into()
})
}
let into_generics_final = if into_generics.is_empty() {
None
}
else {
Some(quote! {
<#(#into_generics),*>
})
};
let index = Index::from(idx);
quote! {
impl #builder_type_generics #builder_name #before {
#[inline(always)]
#[must_use]
#builder_vis fn #plural (mut self, #field_name: #ty) -> #builder_name #before #builder_where_clause{
self.fields.#index.lazy.get_or_insert_with(||core::default::Default::default()).extend(#field_name.into_iter());
self
}
#[inline(always)]
#[must_use]
#builder_vis fn #singular #into_generics_final (mut self, key: #field_key_type, value: #field_value_type) -> #builder_name #before {
self.fields.#index.lazy.get_or_insert_with(||core::default::Default::default()).insert(key #field_key_into_call, value #field_value_into_call);
self
}
}
}
},
_ => {
let mut into_generics = None;
let mut into_call = None;
let mut ty = Some(ty.clone());
if f.ty_into {
let into_type = ty.replace(Type::parse("__T"));
let _ = into_generics.insert(Some(quote! {
<__T: Into<#into_type>>
}));
into_call = Some(quote!{
.into()
})
}
quote! {
impl #builder_type_generics #builder_name #before {
#[inline(always)]
#[must_use]
#builder_vis fn #method_name #into_generics(self, #field_name: #ty) -> #builder_name #after {
let #field_name = #field_name #into_call;
#builder_name {
#builder_receiver_move
fields: #new_state,
_phantom: core::default::Default::default()
}
}
}
}
},
}
})
.collect())
}
fn single_plural_names(ident: &Ident) -> (Ident, Ident) {
let plural = format_ident!("{}", ident);
let mut singular = format_ident!("{}", ident.to_string().to_singular());
if plural == singular {
singular = format_ident!("{}_entry", ident);
}
(singular, plural)
}
fn call(name: Ident, params: Vec<Expr>) -> Expr {
Expr::Call(ExprCall {
attrs: Default::default(),
func: Box::new(Expr::Path(name.to_expr_path())),
paren_token: Default::default(),
args: Punctuated::from_iter(params),
})
}
fn params(
ir: &Ir,
idx: usize,
field_name: &Ident,
builder_type_generics: &Generics,
set: Expr,
) -> Expr {
Expr::Tuple(
builder_type_generics
.to_expr_tuple(|idxp, _| {
if idx == idxp {
Expr::Path(field_name.to_expr_path())
} else {
ir.tuple_field(idxp)
}
})
.with_expr(idx, set),
)
}
#[cfg(test)]
mod tests {
use crate::analyze::analyze;
use crate::buildstructor::tests::*;
use crate::codegen::codegen;
use crate::lower::lower;
macro_rules! assert_codegen {
($input:expr) => {
use rust_format::Formatter;
let models = analyze(false, &$input).expect("Analysis failed");
for model in models {
let ir = lower(model.expect("Analysis failed")).expect("Ir failed");
if let Ok(codegen) = codegen(ir) {
let output = rust_format::RustFmt::default()
.format_str(codegen.to_string())
.unwrap();
insta::assert_snapshot!(output);
} else {
panic!("Failed generate code");
}
}
};
}
#[test]
fn single_field_test() {
assert_codegen!(single_field_test_case());
}
#[test]
fn pub_test() {
assert_codegen!(pub_test_case());
}
#[test]
fn multi_field_test() {
assert_codegen!(multi_field_test_case());
}
#[test]
fn generic_test() {
assert_codegen!(generic_test_case());
}
#[test]
fn async_test() {
assert_codegen!(async_test_case());
}
#[test]
fn fallible_test() {
assert_codegen!(fallible_test_case());
}
#[test]
fn into_test() {
assert_codegen!(into_test_case());
}
#[test]
fn into_where_test() {
assert_codegen!(into_where_test_case());
}
#[test]
fn option_test() {
assert_codegen!(option_test_case());
}
#[test]
fn collection_test() {
assert_codegen!(collections_test_case());
}
#[test]
fn collection_generics_test() {
assert_codegen!(collections_generics_test_case());
}
#[test]
fn collection_option_test() {
assert_codegen!(collections_option_test_case());
}
#[test]
fn returns_self_test() {
assert_codegen!(returns_self_test_case());
}
#[test]
fn multiple_generics_test() {
assert_codegen!(multiple_generics_test_case());
}
#[test]
fn collection_generics_test2() {
assert_codegen!(collections_generics_test_case2());
}
#[test]
fn self_receiver() {
assert_codegen!(self_receiver_test_case());
}
#[test]
fn doc() {
assert_codegen!(doc_test_case());
}
#[test]
fn reference() {
assert_codegen!(reference_test_case());
}
#[test]
fn self_reference() {
assert_codegen!(self_reference_test_case());
}
#[test]
fn lifetime() {
assert_codegen!(lifetime_test_case());
}
#[test]
fn specialization() {
assert_codegen!(specialization_test_case());
}
#[test]
fn specialization_self() {
assert_codegen!(specialization_returns_self_test_case());
}
#[test]
fn associated_types() {
assert_codegen!(associated_types_test_case());
}
}