#![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;
#[proc_macro]
pub fn gen_oop(input: TokenStream) -> TokenStream {
let self_ident = format_ident!("self");
let this_ident = format_ident!("this");
let items = parse_macro_input!(input as syn::File).items;
let Item::Struct(mut main_struct) = items.first().expect("pls impl main struct").clone() else {
panic!("first item is not a struct");
};
let MainStructProperties {
default,
base_names,
vis,
name,
inner_name,
outer_name,
default_vtable_name,
field_names,
} = MainStructProperties::parse(&mut main_struct);
let Item::Struct(mut layout_struct) = items.get(1).expect("pls impl layout struct").clone()
else {
panic!("second item is not a struct");
};
let LayoutStructProps {
layout_name,
layout_fns,
vtable,
} = LayoutStructProps::parse(&mut layout_struct, &name, &self_ident);
let Item::Impl(mut impl_inner) = items.get(2).expect("pls impl inner trait").clone() else {
panic!("expected impl");
};
let (impl_bases_inner, vtable) = parse_impl_bases_inner(&items, &name, &base_names, vtable);
add_main_fields(&mut main_struct.fields, &impl_bases_inner, vtable);
add_layout_fields(&mut layout_struct.fields, &impl_bases_inner);
let layout_struct = if vtable {
layout_struct.to_token_stream()
} else {
quote!()
};
let base_inner_dummy_impls = gen_base_inner_dummy_impls(&impl_bases_inner);
let inner_trait = gen_inner_trait(&vis, &inner_name, &layout_fns, &impl_bases_inner);
let outer_trait = gen_outer_trait(&vis, &outer_name, &layout_fns, &impl_bases_inner);
move_impls_to_main(&mut impl_inner, &impl_bases_inner);
let outer_impl = gen_outer_impl(
&name,
&inner_name,
&outer_name,
&layout_fns,
&impl_bases_inner,
&self_ident,
);
let vtable_getter = gen_vtable_getter(&name, &layout_name, &impl_bases_inner, vtable);
let outer_ptr_impls = gen_outer_ptr_impls(
&name,
&outer_name,
&layout_fns,
&impl_bases_inner,
&self_ident,
&this_ident,
);
let base_init_line = gen_base_init_line(&name, &inner_name, &impl_bases_inner, vtable);
let field_line = quote! {#(#field_names: ::std::default::Default::default(),)*};
let construct_with_vtable =
gen_construct_with_vtable(default, &vis, &name, vtable, &base_init_line, &field_line);
let default_vtable = gen_default_vtable(
&name,
&inner_name,
&default_vtable_name,
&layout_name,
&layout_fns,
&impl_bases_inner,
vtable,
);
let construct_self =
gen_construct_self(vtable, &impl_bases_inner, &base_init_line, &field_line);
let default_impl = gen_default_impl(default, &name, &construct_self);
let cast_ptr = gen_cast_ptr(&name, &impl_bases_inner);
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()
}