cpp_oop_macros 0.1.9

helper crate for 'oop_cpp' crate
Documentation
#![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};

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 inner_impl) = items.get(2).expect("pls impl inner trait").clone() else {
        panic!("expected impl");
    };

    let generics = inner_impl.generics.clone();
    let Type::Path(self_ty) = &*inner_impl.self_ty else {
        panic!("expected path type");
    };
    let generic_args = self_ty.path.segments.first().unwrap().arguments.clone();

    // 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, &generics, &impl_bases_inner);

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

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

    // generate outer impl
    let outer_impl = gen_outer_impl(
        &name,
        &inner_name,
        &outer_name,
        &layout_fns,
        &generics,
        &generic_args,
        &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,
        &generics,
        &generic_args,
        &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
        #inner_impl
        #outer_impl
        #vtable_getter
        #outer_ptr_impls
        #construct_with_vtable
        #default_vtable
        #default_impl
        #cast_ptr
    }
    .into()
}