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
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, ToTokens};
use syn::{Error, Ident, Visibility};

use crate::widgets::{TopLevelWidget, ViewWidgets, Widget};

#[derive(Default)]
pub(super) struct TokenStreams {
    /// Parsing errors
    pub(super) error: TokenStream2,
    /// Initialize the root widget.
    pub(super) init_root: TokenStream2,
    /// Rename root to the actual widget name.
    pub(super) rename_root: TokenStream2,
    /// The tokens for the struct fields -> name: Type,
    pub(super) struct_fields: TokenStream2,
    /// The tokens initializing the widgets.
    pub(super) init: TokenStream2,
    /// The tokens initializing the properties.
    pub(super) assign: TokenStream2,
    /// The tokens for the returned struct fields -> name,
    pub(super) return_fields: TokenStream2,
    /// For destructuring the widget struct field
    pub(super) destructure_fields: TokenStream2,
    /// The view tokens (watch! macro)
    pub(super) update_view: TokenStream2,
}

pub(super) struct TraitImplDetails {
    /// The visibility of the widgets struct.
    pub(super) vis: Option<Visibility>,
    /// The name of the model.
    pub(super) model_name: Ident,
    /// The name of the root widget.
    pub(super) root_name: Option<Ident>,
    /// The name of the sender used in the init function.
    pub(super) sender_name: Ident,
}

impl ViewWidgets {
    pub(super) fn generate_streams(
        &self,
        trait_impl_details: &TraitImplDetails,
        standalone_view: bool,
    ) -> TokenStreams {
        let mut streams = TokenStreams::default();

        for top_level_widget in &self.top_level_widgets {
            top_level_widget.generate_streams(&mut streams, trait_impl_details, standalone_view);
        }

        streams
    }

    /// Get the root widget
    pub(super) fn get_root_widget(&self) -> syn::Result<&Widget> {
        self.top_level_widgets
            .iter()
            .find(|w| w.root_attr.is_some())
            .map(|w| &w.inner)
            .ok_or_else(|| {
                Error::new(
                    self.span,
                    "You need to specify the root widget using the `#[root]` attribute.",
                )
            })
    }

    /// Generate root type for `Root` parameter in `Component` impl
    pub(super) fn root_type(&self) -> TokenStream2 {
        match self.get_root_widget() {
            Ok(root_widget) => root_widget.func_type_token_stream(),
            Err(err) => err.to_compile_error(),
        }
    }

    /// Get the name of the root widget
    pub(super) fn root_name(&self) -> TokenStream2 {
        match self.get_root_widget() {
            Ok(root_widget) => root_widget.name.to_token_stream(),
            Err(err) => err.to_compile_error(),
        }
    }
}

impl TopLevelWidget {
    fn generate_streams(
        &self,
        streams: &mut TokenStreams,
        trait_impl_details: &TraitImplDetails,
        standalone_view: bool,
    ) {
        let generate_init_root_stream = !standalone_view && self.root_attr.is_some();
        self.inner
            .init_token_generation(streams, trait_impl_details, generate_init_root_stream);
    }
}

impl Widget {
    pub(super) fn init_token_generation(
        &self,
        streams: &mut TokenStreams,
        trait_impl_details: &TraitImplDetails,
        generate_root_init_stream: bool,
    ) {
        let TraitImplDetails {
            vis,
            model_name,
            root_name,
            sender_name,
        } = trait_impl_details;

        let name = &self.name;

        // Initialize the root
        if generate_root_init_stream {
            // For the `component` macro
            self.init_root_init_streams(&mut streams.init_root, &mut streams.init);
        } else {
            // For the `view!` macro
            self.init_stream(&mut streams.init);
        }

        #[cfg(feature = "relm4")]
        streams.assign.extend(quote::quote! {
            use relm4::RelmContainerExt as _;
        });

        self.error_stream(&mut streams.error);
        self.start_assign_stream(&mut streams.assign, sender_name);
        self.init_conditional_init_stream(&mut streams.assign, model_name);
        self.struct_fields_stream(&mut streams.struct_fields, vis);
        self.return_stream(&mut streams.return_fields);
        self.destructure_stream(&mut streams.destructure_fields);
        self.init_update_view_stream(&mut streams.update_view, model_name);

        // Rename the `root` to the actual widget name
        if generate_root_init_stream {
            let mut_token = self.mutable.as_ref();
            if let Some(root_name) = root_name {
                streams.rename_root.extend(quote! {
                    let #mut_token #name = #root_name.clone();
                });
            }
        }
    }
}