summaryrefslogtreecommitdiff
path: root/src/generate/into_heads.rs
blob: 5784e46c80d6058e1ebbc292db462eee5f68f252 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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)
}