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 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 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 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}