diff options
Diffstat (limited to 'src/generate/try_constructor.rs')
-rw-r--r-- | src/generate/try_constructor.rs | 301 |
1 files changed, 0 insertions, 301 deletions
diff --git a/src/generate/try_constructor.rs b/src/generate/try_constructor.rs deleted file mode 100644 index 4078c56..0000000 --- a/src/generate/try_constructor.rs +++ /dev/null @@ -1,301 +0,0 @@ -use crate::{ - info_structures::{ArgType, BuilderType, FieldType, Options, StructInfo}, - utils::to_class_case, -}; -use proc_macro2::{Ident, TokenStream}; -use quote::{format_ident, quote}; -use syn::Error; - -pub fn create_try_builder_and_constructor( - info: &StructInfo, - options: Options, - builder_type: BuilderType, -) -> Result<(Ident, TokenStream, TokenStream), Error> { - let struct_name = info.ident.clone(); - let generic_args = info.generic_arguments(); - - let visibility = if options.do_pub_extras { - info.vis.clone() - } else { - syn::parse_quote! { pub(super) } - }; - let mut head_recover_code = Vec::new(); - for field in &info.fields { - if !field.self_referencing { - let field_name = &field.name; - head_recover_code.push(quote! { #field_name }); - } - } - for (_ty, ident) in info.generic_consumers() { - head_recover_code.push(quote! { #ident: ::core::marker::PhantomData }); - } - let mut current_head_index = 0; - - let builder_struct_name = match builder_type { - BuilderType::AsyncSend => format_ident!("{}AsyncSendTryBuilder", info.ident), - BuilderType::Async => format_ident!("{}AsyncTryBuilder", info.ident), - BuilderType::Sync => format_ident!("{}TryBuilder", info.ident), - }; - let documentation = format!( - concat!( - "(See also [`{0}::try_build()`]({0}::try_build).) Like [`new`](Self::new), but ", - "builders for [self-referencing fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) ", - "can return results. If any of them fail, `Err` is returned. If all of them ", - "succeed, `Ok` is returned. The arguments are as follows:\n\n", - "| Argument | Suggested Use |\n| --- | --- |\n", - ), - builder_struct_name.to_string() - ); - let or_recover_documentation = format!( - concat!( - "(See also [`{0}::try_build_or_recover()`]({0}::try_build_or_recover).) Like ", - "[`try_new`](Self::try_new), but all ", - "[head fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) ", - "are returned in the case of an error. The arguments are as follows:\n\n", - "| Argument | Suggested Use |\n| --- | --- |\n", - ), - builder_struct_name.to_string() - ); - let builder_documentation = concat!( - "A more verbose but stable way to construct self-referencing structs. It is ", - "comparable to using `StructName { field1: value1, field2: value2 }` rather than ", - "`StructName::new(value1, value2)`. This has the dual benefit of makin your code ", - "both easier to refactor and more readable. Call [`try_build()`](Self::try_build) or ", - "[`try_build_or_recover()`](Self::try_build_or_recover) to ", - "construct the actual struct. The fields of this struct should be used as follows:\n\n", - "| Field | Suggested Use |\n| --- | --- |\n", - ) - .to_owned(); - let build_fn_documentation = format!( - concat!( - "Calls [`{0}::try_new()`]({0}::try_new) using the provided values. This is ", - "preferrable over calling `try_new()` directly for the reasons listed above. " - ), - info.ident.to_string() - ); - let build_or_recover_fn_documentation = format!( - concat!( - "Calls [`{0}::try_new_or_recover()`]({0}::try_new_or_recover) using the provided ", - "values. This is preferrable over calling `try_new_or_recover()` directly for the ", - "reasons listed above. " - ), - info.ident.to_string() - ); - let mut doc_table = "".to_owned(); - let mut or_recover_code: Vec<TokenStream> = Vec::new(); - let mut params: Vec<TokenStream> = Vec::new(); - let mut builder_struct_generic_producers: Vec<_> = info - .generic_params() - .iter() - .map(|param| quote! { #param }) - .collect(); - let mut builder_struct_generic_consumers = info.generic_arguments(); - let mut builder_struct_fields = Vec::new(); - let mut builder_struct_field_names = Vec::new(); - - for field in &info.fields { - let field_name = &field.name; - - let arg_type = field.make_try_constructor_arg_type(info, builder_type)?; - if let ArgType::Plain(plain_type) = arg_type { - // No fancy builder function, we can just move the value directly into the struct. - params.push(quote! { #field_name: #plain_type }); - builder_struct_fields.push(quote! { #field_name: #plain_type }); - builder_struct_field_names.push(quote! { #field_name }); - doc_table += &format!( - "| `{}` | Directly pass in the value this field should contain |\n", - field_name.to_string() - ); - if !field.self_referencing { - if field.is_borrowed() { - head_recover_code[current_head_index] = quote! { - #field_name: ::ouroboros::macro_help::unbox(#field_name) - }; - } else { - head_recover_code[current_head_index] = quote! { #field_name }; - } - current_head_index += 1; - } - } else if let ArgType::TraitBound(bound_type) = arg_type { - // Trait bounds are much trickier. We need a special syntax to accept them in the - // contructor, and generic parameters need to be added to the builder struct to make - // it work. - let builder_name = field.builder_name(); - params.push(quote! { #builder_name : impl #bound_type }); - // Ok so hear me out basically without this thing here my IDE thinks the rest of the - // code is a string and it all turns green. - {} - doc_table += &format!( - "| `{}` | Use a function or closure: `(", - builder_name.to_string() - ); - let mut builder_args = Vec::new(); - for (index, borrow) in field.borrows.iter().enumerate() { - let borrowed_name = &info.fields[borrow.index].name; - builder_args.push(format_ident!("{}_illegal_static_reference", borrowed_name)); - doc_table += &format!( - "{}: &{}_", - borrowed_name.to_string(), - if borrow.mutable { "mut " } else { "" }, - ); - if index < field.borrows.len() - 1 { - doc_table += ", "; - } - } - doc_table += &format!(") -> Result<{}: _, Error_>` | \n", field_name.to_string()); - let builder_value = if builder_type.is_async() { - quote! { #builder_name (#(#builder_args),*).await } - } else { - quote! { #builder_name (#(#builder_args),*) } - }; - or_recover_code.push(quote! { - let #field_name = match #builder_value { - ::core::result::Result::Ok(value) => value, - ::core::result::Result::Err(err) - => return ::core::result::Result::Err((err, Heads { #(#head_recover_code),* })), - }; - }); - let generic_type_name = - format_ident!("{}Builder_", to_class_case(field_name.to_string().as_str())); - - builder_struct_generic_producers.push(quote! { #generic_type_name: #bound_type }); - builder_struct_generic_consumers.push(quote! { #generic_type_name }); - builder_struct_fields.push(quote! { #builder_name: #generic_type_name }); - builder_struct_field_names.push(quote! { #builder_name }); - } - if field.is_borrowed() { - let boxed = field.boxed(); - if field.field_type == FieldType::BorrowedMut { - or_recover_code.push(quote! { let mut #field_name = #boxed; }); - } else { - or_recover_code.push(quote! { let #field_name = #boxed; }); - } - } - - if field.field_type == FieldType::Borrowed { - or_recover_code.push(field.make_illegal_static_reference()); - } else if field.field_type == FieldType::BorrowedMut { - or_recover_code.push(field.make_illegal_static_mut_reference()); - } - } - let documentation = if !options.do_no_doc { - let documentation = documentation + &doc_table; - quote! { - #[doc=#documentation] - } - } else { - quote! { #[doc(hidden)] } - }; - let or_recover_documentation = if !options.do_no_doc { - let or_recover_documentation = or_recover_documentation + &doc_table; - quote! { - #[doc=#or_recover_documentation] - } - } else { - quote! { #[doc(hidden)] } - }; - let builder_documentation = if !options.do_no_doc { - let builder_documentation = builder_documentation + &doc_table; - quote! { - #[doc=#builder_documentation] - } - } else { - quote! { #[doc(hidden)] } - }; - let or_recover_ident = match builder_type { - BuilderType::AsyncSend => quote! { try_new_or_recover_async_send }, - BuilderType::Async => quote! { try_new_or_recover_async }, - BuilderType::Sync => quote! { try_new_or_recover }, - }; - let or_recover_constructor_fn = if builder_type.is_async() { - quote! { async fn #or_recover_ident } - } else { - quote! { fn #or_recover_ident } - }; - let constructor_fn = match builder_type { - BuilderType::AsyncSend => quote! { async fn try_new_async_send }, - BuilderType::Async => quote! { async fn try_new_async }, - BuilderType::Sync => quote! { fn try_new }, - }; - let constructor_code = if builder_type.is_async() { - quote! { #struct_name::#or_recover_ident(#(#builder_struct_field_names),*).await.map_err(|(error, _heads)| error) } - } else { - quote! { #struct_name::#or_recover_ident(#(#builder_struct_field_names),*).map_err(|(error, _heads)| error) } - }; - let field_names: Vec<_> = info.fields.iter().map(|field| field.name.clone()).collect(); - let constructor_def = quote! { - #documentation - #visibility #constructor_fn<Error_>(#(#params),*) -> ::core::result::Result<#struct_name <#(#generic_args),*>, Error_> { - #constructor_code - } - #or_recover_documentation - #visibility #or_recover_constructor_fn<Error_>(#(#params),*) -> ::core::result::Result<#struct_name <#(#generic_args),*>, (Error_, Heads<#(#generic_args),*>)> { - #(#or_recover_code)* - ::core::result::Result::Ok(Self { #(#field_names),* }) - } - }; - builder_struct_generic_producers.push(quote! { Error_ }); - builder_struct_generic_consumers.push(quote! { Error_ }); - let generic_where = &info.generics.where_clause; - let builder_fn = if builder_type.is_async() { - quote! { async fn try_build } - } else { - quote! { fn try_build } - }; - let or_recover_builder_fn = if builder_type.is_async() { - quote! { async fn try_build_or_recover } - } else { - quote! { fn try_build_or_recover } - }; - let builder_code = match builder_type { - BuilderType::AsyncSend => quote! { - #struct_name::try_new_async_send( - #(self.#builder_struct_field_names),* - ).await - }, - BuilderType::Async => quote! { - #struct_name::try_new_async( - #(self.#builder_struct_field_names),* - ).await - }, - BuilderType::Sync => quote! { - #struct_name::try_new( - #(self.#builder_struct_field_names),* - ) - }, - }; - let or_recover_builder_code = match builder_type { - BuilderType::AsyncSend => quote! { - #struct_name::try_new_or_recover_async_send( - #(self.#builder_struct_field_names),* - ).await - }, - BuilderType::Async => quote! { - #struct_name::try_new_or_recover_async( - #(self.#builder_struct_field_names),* - ).await - }, - BuilderType::Sync => quote! { - #struct_name::try_new_or_recover( - #(self.#builder_struct_field_names),* - ) - }, - }; - let builder_def = quote! { - #builder_documentation - #visibility struct #builder_struct_name <#(#builder_struct_generic_producers),*> #generic_where { - #(#visibility #builder_struct_fields),* - } - impl<#(#builder_struct_generic_producers),*> #builder_struct_name <#(#builder_struct_generic_consumers),*> #generic_where { - #[doc=#build_fn_documentation] - #visibility #builder_fn(self) -> ::core::result::Result<#struct_name <#(#generic_args),*>, Error_> { - #builder_code - } - #[doc=#build_or_recover_fn_documentation] - #visibility #or_recover_builder_fn(self) -> ::core::result::Result<#struct_name <#(#generic_args),*>, (Error_, Heads<#(#generic_args),*>)> { - #or_recover_builder_code - } - } - }; - Ok((builder_struct_name, builder_def, constructor_def)) -} |