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
use proc_macro2::{Span as Span2, TokenStream as TokenStream2};
use quote::quote;
use syn::{spanned::Spanned, Error, Ident, ImplItem, ItemImpl, Visibility};

use crate::{
    token_streams::{TokenStreams, TraitImplDetails},
    widgets::ViewWidgets,
};

pub(crate) fn generate_tokens(vis: Option<Visibility>, mut item_impl: ItemImpl) -> TokenStream2 {
    if item_impl.items.len() != 1 {
        return Error::new(
            item_impl.span(),
            "Expected only one view macro and nothing else",
        )
        .into_compile_error();
    }

    let item = item_impl.items.pop().unwrap();

    if let ImplItem::Macro(mac) = item {
        if Some("view") == mac.mac.path.get_ident().map(|i| i.to_string()).as_deref() {
            match syn::parse_macro_input::parse::<ViewWidgets>(mac.mac.tokens.into()) {
                Ok(mut view_widgets) => {
                    view_widgets.mark_root_as_used();

                    let TokenStreams {
                        error,
                        init,
                        assign,
                        struct_fields,
                        return_fields,
                        ..
                    } = view_widgets.generate_streams(
                        &TraitImplDetails {
                            vis: vis.clone(),
                            model_name: Ident::new("_", Span2::call_site()),
                            sender_name: Ident::new("sender", Span2::call_site()),
                            root_name: None,
                        },
                        true,
                    );

                    let view_output = quote! {
                        #init
                        #assign
                        {
                            #error
                        }
                    };

                    let root_widget_type = view_widgets.root_type();
                    item_impl.items.push(ImplItem::Verbatim(quote! {
                        type Root = #root_widget_type;
                    }));

                    let root_name = view_widgets.root_name();

                    item_impl.items.push(ImplItem::Verbatim(quote! {
                        fn init() -> Self {
                            #view_output
                            Self {
                                #return_fields
                            }
                        }
                    }));

                    let type_name = &item_impl.self_ty;

                    quote! {
                        #[derive(Debug, Clone)]
                        #vis struct #type_name {
                            #struct_fields
                        }

                        impl ::std::ops::Deref for #type_name {
                            type Target = #root_widget_type;

                            fn deref(&self) -> &Self::Target {
                                &self.#root_name
                            }
                        }

                        #item_impl
                    }
                }
                Err(err) => err.to_compile_error(),
            }
        } else {
            Error::new(mac.mac.path.span(), "Expected a view macro").into_compile_error()
        }
    } else {
        Error::new(item.span(), "Expected a macro").into_compile_error()
    }
}