ouroboros_macro/generate/
into_heads.rs

1use proc_macro2::TokenStream;
2use quote::quote;
3
4use crate::info_structures::{Options, StructInfo};
5
6/// Returns the Heads struct and a function to convert the original struct into a Heads instance.
7pub fn make_into_heads(info: &StructInfo, options: Options) -> (TokenStream, TokenStream) {
8    let visibility = if options.do_pub_extras {
9        info.vis.clone()
10    } else {
11        syn::parse_quote! { pub(super) }
12    };
13    let mut code = Vec::new();
14    let mut field_initializers = Vec::new();
15    let mut head_fields = Vec::new();
16    let internal_struct = &info.internal_ident;
17    // Drop everything in the reverse order of what it was declared in. Fields that come later
18    // are only dependent on fields that came before them.
19    for field in info.fields.iter().rev() {
20        let field_name = &field.name;
21        if field.self_referencing {
22            // Heads are fields that do not borrow anything.
23            code.push(quote! { ::core::mem::drop(this.#field_name); });
24        } else {
25            code.push(quote! { let #field_name = this.#field_name; });
26            if field.is_borrowed() {
27                field_initializers
28                    .push(quote! { #field_name: ::ouroboros::macro_help::unbox(#field_name) });
29            } else {
30                field_initializers.push(quote! { #field_name });
31            }
32            let field_type = &field.typ;
33            head_fields.push(quote! { #visibility #field_name: #field_type });
34        }
35    }
36    for (ty, ident) in info.generic_consumers() {
37        head_fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> });
38        field_initializers.push(quote! { #ident: ::core::marker::PhantomData });
39    }
40    let documentation = format!(
41        concat!(
42            "A struct which contains only the ",
43            "[head fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) of [`{0}`]({0})."
44        ),
45        info.ident.to_string()
46    );
47    let generic_params = info.generic_params();
48    let generic_where = &info.generics.where_clause;
49    let heads_struct_def = quote! {
50        #[doc=#documentation]
51        #visibility struct Heads <#generic_params> #generic_where {
52            #(#head_fields),*
53        }
54    };
55    let documentation = concat!(
56        "This function drops all internally referencing fields and returns only the ",
57        "[head fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) of this struct."
58    ).to_owned();
59
60    let documentation = if !options.do_no_doc {
61        quote! {
62            #[doc=#documentation]
63        }
64    } else {
65        quote! { #[doc(hidden)] }
66    };
67
68    let generic_args = info.generic_arguments();
69    let into_heads_fn = quote! {
70        #documentation
71        #[allow(clippy::drop_ref)]
72        #[allow(clippy::drop_copy)]
73        #[allow(clippy::drop_non_drop)]
74        #visibility fn into_heads(self) -> Heads<#(#generic_args),*> {
75            let this_ptr = &self as *const _;
76            let this: #internal_struct<#(#generic_args),*> = unsafe { ::core::mem::transmute_copy(&*this_ptr) };
77            ::core::mem::forget(self);
78            #(#code)*
79            Heads {
80                #(#field_initializers),*
81            }
82        }
83    };
84    (heads_struct_def, into_heads_fn)
85}