ouroboros_macro/generate/
constructor.rs

1use crate::{
2    info_structures::{ArgType, BuilderType, FieldType, Options, StructInfo},
3    utils::to_class_case,
4};
5use proc_macro2::{Ident, TokenStream};
6use quote::{format_ident, quote};
7use syn::Error;
8
9pub fn create_builder_and_constructor(
10    info: &StructInfo,
11    options: Options,
12    builder_type: BuilderType,
13) -> Result<(Ident, TokenStream, TokenStream), Error> {
14    let struct_name = info.ident.clone();
15    let generic_args = info.generic_arguments();
16
17    let vis = if options.do_pub_extras {
18        info.vis.clone()
19    } else {
20        syn::parse_quote! { pub(super) }
21    };
22    let builder_struct_name = match builder_type {
23        BuilderType::AsyncSend => format_ident!("{}AsyncSendBuilder", info.ident),
24        BuilderType::Async => format_ident!("{}AsyncBuilder", info.ident),
25        BuilderType::Sync => format_ident!("{}Builder", info.ident),
26    };
27    let documentation = format!(
28        concat!(
29            "Constructs a new instance of this self-referential struct. (See also ",
30            "[`{0}::build()`]({0}::build)). Each argument is a field of ",
31            "the new struct. Fields that refer to other fields inside the struct are initialized ",
32            "using functions instead of directly passing their value. The arguments are as ",
33            "follows:\n\n| Argument | Suggested Use |\n| --- | --- |\n",
34        ),
35        builder_struct_name.to_string()
36    );
37    let builder_documentation = concat!(
38        "A more verbose but stable way to construct self-referencing structs. It is ",
39        "comparable to using `StructName { field1: value1, field2: value2 }` rather than ",
40        "`StructName::new(value1, value2)`. This has the dual benefit of making your code ",
41        "both easier to refactor and more readable. Call [`build()`](Self::build) to ",
42        "construct the actual struct. The fields of this struct should be used as follows:\n\n",
43        "| Field | Suggested Use |\n| --- | --- |\n",
44    )
45    .to_owned();
46    let build_fn_documentation = format!(
47        concat!(
48            "Calls [`{0}::new()`]({0}::new) using the provided values. This is preferable over ",
49            "calling `new()` directly for the reasons listed above. "
50        ),
51        info.ident.to_string()
52    );
53    let mut doc_table = "".to_owned();
54    let mut code: Vec<TokenStream> = Vec::new();
55    let mut params: Vec<TokenStream> = Vec::new();
56    let mut builder_struct_generic_producers: Vec<_> = info
57        .generic_params()
58        .iter()
59        .map(|param| quote! { #param })
60        .collect();
61    let mut builder_struct_generic_consumers = info.generic_arguments();
62    let mut builder_struct_fields = Vec::new();
63    let mut builder_struct_field_names = Vec::new();
64
65    // code.push(quote! { let mut result = ::core::mem::MaybeUninit::<Self>::uninit(); });
66
67    for field in &info.fields {
68        let field_name = &field.name;
69
70        let arg_type = field.make_constructor_arg_type(info, builder_type)?;
71        if let ArgType::Plain(plain_type) = arg_type {
72            // No fancy builder function, we can just move the value directly into the struct.
73            params.push(quote! { #field_name: #plain_type });
74            builder_struct_fields.push(quote! { #field_name: #plain_type });
75            builder_struct_field_names.push(quote! { #field_name });
76            doc_table += &format!(
77                "| `{}` | Directly pass in the value this field should contain |\n",
78                field_name
79            );
80        } else if let ArgType::TraitBound(bound_type) = arg_type {
81            // Trait bounds are much trickier. We need a special syntax to accept them in the
82            // constructor, and generic parameters need to be added to the builder struct to make
83            // it work.
84            let builder_name = field.builder_name();
85            params.push(quote! { #builder_name : impl #bound_type });
86            doc_table += &format!(
87                "| `{}` | Use a function or closure: `(",
88                builder_name
89            );
90            let mut builder_args = Vec::new();
91            for (index, borrow) in field.borrows.iter().enumerate() {
92                let borrowed_name = &info.fields[borrow.index].name;
93                builder_args.push(format_ident!("{}_illegal_static_reference", borrowed_name));
94                doc_table += &format!(
95                    "{}: &{}_",
96                    borrowed_name,
97                    if borrow.mutable { "mut " } else { "" },
98                );
99                if index < field.borrows.len() - 1 {
100                    doc_table += ", ";
101                }
102            }
103            doc_table += &format!(") -> {}: _` | \n", field_name);
104            if builder_type.is_async() {
105                code.push(quote! { let #field_name = #builder_name (#(#builder_args),*).await; });
106            } else {
107                code.push(quote! { let #field_name = #builder_name (#(#builder_args),*); });
108            }
109            let generic_type_name =
110                format_ident!("{}Builder_", to_class_case(field_name.to_string().as_str()));
111
112            builder_struct_generic_producers.push(quote! { #generic_type_name: #bound_type });
113            builder_struct_generic_consumers.push(quote! { #generic_type_name });
114            builder_struct_fields.push(quote! { #builder_name: #generic_type_name });
115            builder_struct_field_names.push(quote! { #builder_name });
116        }
117        if field.is_borrowed() {
118            let boxed = field.boxed();
119            if field.field_type == FieldType::BorrowedMut {
120                code.push(quote! { let mut #field_name = #boxed; });
121            } else {
122                code.push(quote! { let #field_name = #boxed; });
123            }
124        };
125
126        if field.field_type == FieldType::Borrowed {
127            code.push(field.make_illegal_static_reference());
128        } else if field.field_type == FieldType::BorrowedMut {
129            code.push(field.make_illegal_static_mut_reference());
130        }
131    }
132
133    let documentation = if !options.do_no_doc {
134        let documentation = documentation + &doc_table;
135        quote! {
136            #[doc=#documentation]
137        }
138    } else {
139        quote! { #[doc(hidden)] }
140    };
141
142    let builder_documentation = if !options.do_no_doc {
143        let builder_documentation = builder_documentation + &doc_table;
144        quote! {
145            #[doc=#builder_documentation]
146        }
147    } else {
148        quote! { #[doc(hidden)] }
149    };
150
151    let constructor_fn = match builder_type {
152        BuilderType::AsyncSend => quote! { async fn new_async_send },
153        BuilderType::Async => quote! { async fn new_async },
154        BuilderType::Sync => quote! { fn new },
155    };
156    let field_names: Vec<_> = info.fields.iter().map(|field| field.name.clone()).collect();
157    let internal_ident = &info.internal_ident;
158    let constructor_def = quote! {
159        #documentation
160        #vis #constructor_fn(#(#params),*) -> #struct_name <#(#generic_args),*> {
161            #(#code)*
162            unsafe {
163                Self {
164                    actual_data: ::core::mem::MaybeUninit::new(#internal_ident {
165                        #(#field_names),*
166                    })
167                }
168            }
169        }
170    };
171    let generic_where = &info.generics.where_clause;
172    let builder_fn = if builder_type.is_async() {
173        quote! { async fn build }
174    } else {
175        quote! { fn build }
176    };
177    let builder_code = match builder_type {
178        BuilderType::AsyncSend => quote! {
179            #struct_name::new_async_send(
180                #(self.#builder_struct_field_names),*
181            ).await
182        },
183        BuilderType::Async => quote! {
184            #struct_name::new_async(
185                #(self.#builder_struct_field_names),*
186            ).await
187        },
188        BuilderType::Sync => quote! {
189            #struct_name::new(
190                #(self.#builder_struct_field_names),*
191            )
192        },
193    };
194    let builder_def = quote! {
195        #builder_documentation
196        #vis struct #builder_struct_name <#(#builder_struct_generic_producers),*> #generic_where {
197            #(#vis #builder_struct_fields),*
198        }
199        impl<#(#builder_struct_generic_producers),*> #builder_struct_name <#(#builder_struct_generic_consumers),*> #generic_where {
200            #[doc=#build_fn_documentation]
201            #vis #builder_fn(self) -> #struct_name <#(#generic_args),*> {
202                #builder_code
203            }
204        }
205    };
206    Ok((builder_struct_name, builder_def, constructor_def))
207}