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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#![feature(let_chains)]
mod handlers;
mod utils;

use handlers::actions::{add_layout_fields, add_main_fields, move_impls_to_main};
use handlers::generators::{
    gen_base_init_line, gen_base_inner_dummy_impls, gen_cast_ptr, gen_construct_self,
    gen_construct_with_vtable, gen_default_impl, gen_default_vtable, gen_inner_trait,
    gen_outer_impl, gen_outer_ptr_impls, gen_outer_trait, gen_vtable_getter,
};
use handlers::parsers::{parse_impl_bases_inner, LayoutStructProps, MainStructProperties};
use proc_macro::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::{parse_macro_input, Item};

type TokenStream2 = proc_macro2::TokenStream;

/// inner trait is the one that user defines in the layout struct
/// outer trait is auto-defined by the macro and ALWAYS does the static dispatch
/// vtable getter is required for the traits described bellow
/// outer ptr traits are THE ONLY things that will use the vtable when should
/// # Panics
/// when user definition is incomplete or incorrect
#[proc_macro]
pub fn gen_oop(input: TokenStream) -> TokenStream {
    // it is just used so often
    let self_ident = format_ident!("self");

    let this_ident = format_ident!("this");

    // parse all the items in a macro call
    let items = parse_macro_input!(input as syn::File).items;

    // parse main struct
    let Item::Struct(mut main_struct) = items.first().expect("pls impl main struct").clone() else {
        panic!("first item is not a struct");
    };

    // parse main struct properties
    let MainStructProperties {
        default,
        base_names,
        vis,
        name,
        inner_name,
        outer_name,
        default_vtable_name,
        field_names,
    } = MainStructProperties::parse(&mut main_struct);

    // parse layout struct
    let Item::Struct(mut layout_struct) = items.get(1).expect("pls impl layout struct").clone()
    else {
        panic!("second item is not a struct");
    };

    // parse layout struct properties
    let LayoutStructProps {
        layout_name,
        layout_fns,
        vtable,
    } = LayoutStructProps::parse(&mut layout_struct, &name, &self_ident);

    // make sure user impls inner trait
    let Item::Impl(mut impl_inner) = items.get(2).expect("pls impl inner trait").clone() else {
        panic!("expected impl");
    };

    // make sure the user impls base's trait
    let (impl_bases_inner, vtable) = parse_impl_bases_inner(&items, &name, &base_names, vtable);

    // add proper fields to the main struct
    add_main_fields(&mut main_struct.fields, &impl_bases_inner, vtable);

    // add proper fields to the layout struct
    add_layout_fields(&mut layout_struct.fields, &impl_bases_inner);

    // ignore layout struct if there is no vtable
    let layout_struct = if vtable {
        layout_struct.to_token_stream()
    } else {
        quote!()
    };

    // check for correctness of user-defined base inner impls
    let base_inner_dummy_impls = gen_base_inner_dummy_impls(&impl_bases_inner);

    // generate inner trait
    let inner_trait = gen_inner_trait(&vis, &inner_name, &layout_fns, &impl_bases_inner);

    // generate outer trait
    let outer_trait = gen_outer_trait(&vis, &outer_name, &layout_fns, &impl_bases_inner);

    // move user-defined base inner impls to the main inner impl
    move_impls_to_main(&mut impl_inner, &impl_bases_inner);

    // generate outer impl
    let outer_impl = gen_outer_impl(
        &name,
        &inner_name,
        &outer_name,
        &layout_fns,
        &impl_bases_inner,
        &self_ident,
    );

    // generate vtable getter
    let vtable_getter = gen_vtable_getter(&name, &layout_name, &impl_bases_inner, vtable);

    // generate outer ptr impls
    let outer_ptr_impls = gen_outer_ptr_impls(
        &name,
        &outer_name,
        &layout_fns,
        &impl_bases_inner,
        &self_ident,
        &this_ident,
    );

    // generate line in default constructor with vtable, it will take care of bases's vtables
    let base_init_line = gen_base_init_line(&name, &inner_name, &impl_bases_inner, vtable);

    // generate line that takes care of the rest of args in a default constructor
    let field_line = quote! {#(#field_names: ::std::default::Default::default(),)*};

    // generate a fn that creates a default instance with the provided vtable
    let construct_with_vtable =
        gen_construct_with_vtable(default, &vis, &name, vtable, &base_init_line, &field_line);

    // generate fn with static variable that will hold a vtable instance default specifically for our main struct
    let default_vtable = gen_default_vtable(
        &name,
        &inner_name,
        &default_vtable_name,
        &layout_name,
        &layout_fns,
        &impl_bases_inner,
        vtable,
    );

    // generate line that constructs Self
    let construct_self =
        gen_construct_self(vtable, &impl_bases_inner, &base_init_line, &field_line);

    // generate default trait impl
    let default_impl = gen_default_impl(default, &name, &construct_self);

    // generate fns used for casting ptr
    let cast_ptr = gen_cast_ptr(&name, &impl_bases_inner);

    // our final output
    quote! {
        #main_struct
        #layout_struct
        #base_inner_dummy_impls
        #inner_trait
        #outer_trait
        #impl_inner
        #outer_impl
        #vtable_getter
        #outer_ptr_impls
        #construct_with_vtable
        #default_vtable
        #default_impl
        #cast_ptr
    }
    .into()
}