aboutsummaryrefslogtreecommitdiff
path: root/macros/src/codegen/init.rs
blob: b350298c46fa0e7ecefcb30f19abc53b658f9ca5 (plain) (blame)
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
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use rtic_syntax::{ast::App, Context};

use crate::{
    analyze::Analysis,
    check::Extra,
    codegen::{locals, module, resources_struct, util},
};

/// Generates support code for `#[init]` functions
pub fn codegen(
    app: &App,
    analysis: &Analysis,
    extra: &Extra,
) -> (
    // const_app_idle -- the `${init}Resources` constructor
    Option<TokenStream2>,
    // root_init -- items that must be placed in the root of the crate:
    // - the `${init}Locals` struct
    // - the `${init}Resources` struct
    // - the `${init}LateResources` struct
    // - the `${init}` module, which contains types like `${init}::Context`
    Vec<TokenStream2>,
    // user_init -- the `#[init]` function written by the user
    Option<TokenStream2>,
    // user_init_imports -- the imports for `#[init]` functio written by the user
    Vec<TokenStream2>,
    // call_init -- the call to the user `#[init]` if there's one
    Option<TokenStream2>,
) {
    if app.inits.len() > 0 {
        let init = &app.inits.first().unwrap();
        let mut needs_lt = false;
        let name = &init.name;

        let mut root_init = vec![];

        let mut user_init_imports = vec![];

        let ret = {
            let late_fields = analysis
                .late_resources
                .iter()
                .flat_map(|resources| {
                    resources.iter().map(|name| {
                        let ty = &app.late_resources[name].ty;
                        let cfgs = &app.late_resources[name].cfgs;

                        quote!(
                        #(#cfgs)*
                        pub #name: #ty
                        )
                    })
                })
                .collect::<Vec<_>>();

            if !late_fields.is_empty() {
                let late_resources = util::late_resources_ident(&name);

                root_init.push(quote!(
                    /// Resources initialized at runtime
                    #[allow(non_snake_case)]
                    pub struct #late_resources {
                        #(#late_fields),*
                    }
                ));

                let name_late = format_ident!("{}LateResources", name);
                user_init_imports.push(quote!(
                        #[allow(non_snake_case)]
                        use super::#name_late;
                ));

                Some(quote!(-> #name::LateResources))
            } else {
                None
            }
        };

        let mut locals_pat = None;
        let mut locals_new = None;
        if !init.locals.is_empty() {
            let (struct_, pat) = locals::codegen(Context::Init, &init.locals, app);

            locals_new = Some(quote!(#name::Locals::new()));
            locals_pat = Some(pat);
            root_init.push(struct_);
        }

        let context = &init.context;
        let attrs = &init.attrs;
        let stmts = &init.stmts;
        let locals_pat = locals_pat.iter();
        let user_init = Some(quote!(
            #(#attrs)*
            #[allow(non_snake_case)]
            fn #name(#(#locals_pat,)* #context: #name::Context) #ret {
                #(#stmts)*
            }
        ));
        user_init_imports.push(quote!(
                #(#attrs)*
                #[allow(non_snake_case)]
                use super::#name;
        ));

        let mut const_app = None;
        if !init.args.resources.is_empty() {
            let (item, constructor) =
                resources_struct::codegen(Context::Init, 0, &mut needs_lt, app, analysis);

            root_init.push(item);
            const_app = Some(constructor);

            let name_late = format_ident!("{}Resources", name);
            user_init_imports.push(quote!(
                    #[allow(non_snake_case)]
                    use super::#name_late;
            ));
        }

        let locals_new = locals_new.iter();
        let call_init = Some(
            quote!(let late = crate::#name(#(#locals_new,)* #name::Context::new(core.into()));),
        );

        root_init.push(module::codegen(Context::Init, needs_lt, app, extra));

        (
            const_app,
            root_init,
            user_init,
            user_init_imports,
            call_init,
        )
    } else {
        (None, vec![], None, vec![], None)
    }
}