diff options
Diffstat (limited to 'src/generate/into_heads.rs')
-rw-r--r-- | src/generate/into_heads.rs | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/src/generate/into_heads.rs b/src/generate/into_heads.rs new file mode 100644 index 0000000..5784e46 --- /dev/null +++ b/src/generate/into_heads.rs @@ -0,0 +1,81 @@ +use proc_macro2::TokenStream; +use quote::quote; + +use crate::info_structures::{Options, StructInfo}; + +/// Returns the Heads struct and a function to convert the original struct into a Heads instance. +pub fn make_into_heads(info: &StructInfo, options: Options) -> (TokenStream, TokenStream) { + let visibility = if options.do_pub_extras { + info.vis.clone() + } else { + syn::parse_quote! { pub(super) } + }; + let mut code = Vec::new(); + let mut field_initializers = Vec::new(); + let mut head_fields = Vec::new(); + // Drop everything in the reverse order of what it was declared in. Fields that come later + // are only dependent on fields that came before them. + for field in info.fields.iter().rev() { + let field_name = &field.name; + if !field.self_referencing { + code.push(quote! { let #field_name = self.#field_name; }); + if field.is_borrowed() { + field_initializers + .push(quote! { #field_name: ::ouroboros::macro_help::unbox(#field_name) }); + } else { + field_initializers.push(quote! { #field_name }); + } + let field_type = &field.typ; + head_fields.push(quote! { #visibility #field_name: #field_type }); + } else { + // Heads are fields that do not borrow anything. + code.push(quote! { ::core::mem::drop(self.#field_name); }); + } + } + for (ty, ident) in info.generic_consumers() { + head_fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> }); + field_initializers.push(quote! { #ident: ::core::marker::PhantomData }); + } + let documentation = format!( + concat!( + "A struct which contains only the ", + "[head fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) of [`{0}`]({0})." + ), + info.ident.to_string() + ); + let generic_params = info.generic_params(); + let generic_where = &info.generics.where_clause; + let heads_struct_def = quote! { + #[doc=#documentation] + #visibility struct Heads <#generic_params> #generic_where { + #(#head_fields),* + } + }; + let documentation = concat!( + "This function drops all internally referencing fields and returns only the ", + "[head fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) of this struct." + ).to_owned(); + + let documentation = if !options.do_no_doc { + quote! { + #[doc=#documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + + let generic_args = info.generic_arguments(); + let into_heads_fn = quote! { + #documentation + #[allow(clippy::drop_ref)] + #[allow(clippy::drop_copy)] + #[allow(clippy::drop_non_drop)] + #visibility fn into_heads(self) -> Heads<#(#generic_args),*> { + #(#code)* + Heads { + #(#field_initializers),* + } + } + }; + (heads_struct_def, into_heads_fn) +} |