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
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use syn::{parse_quote, Ident};

use crate::delegate_components::ast::DefineComponentsAst;
use crate::delegate_components::define_struct::define_struct;
use crate::delegate_components::delegates_to::define_delegates_to_trait;
use crate::delegate_components::impl_delegate::impl_delegate_components;
use crate::delegate_components::substitution_macro::define_substitution_macro;
use crate::derive_component::snake_case::to_snake_case_str;

pub fn define_components(body: TokenStream) -> TokenStream {
    let ast: DefineComponentsAst = syn::parse2(body).unwrap();

    let components_type = {
        let components_ident = &ast.components_ident;
        let type_generics = ast.components_generics.split_for_impl().1;
        parse_quote!( #components_ident #type_generics )
    };

    let impl_items = impl_delegate_components(
        &components_type,
        &ast.components_generics,
        &ast.delegate_entries,
    );

    let item_struct = define_struct(&ast.components_ident, &ast.components_generics);

    let mut output = TokenStream::new();

    output.extend(item_struct.to_token_stream());

    for impl_item in impl_items {
        output.extend(impl_item.to_token_stream());
    }

    {
        let delegates_to_trait_name = format!("DelegatesTo{}", ast.components_ident);

        let (delegates_to_trait, delegates_to_impl) = define_delegates_to_trait(
            &Ident::new(&delegates_to_trait_name, Span::call_site()),
            &components_type,
            &ast.components_generics,
            &ast.delegate_entries,
        );

        output.extend(delegates_to_trait.to_token_stream());
        output.extend(delegates_to_impl.to_token_stream());
    }

    {
        let with_components_macro_name = format!(
            "with_{}",
            to_snake_case_str(&ast.components_ident.to_string())
        );

        let with_components_macro = define_substitution_macro(
            &Ident::new(&with_components_macro_name, Span::call_site()),
            &ast.components_ident,
            &ast.delegate_entries.all_components().to_token_stream(),
        );

        output.extend(with_components_macro);
    }

    output
}