scuffle_bootstrap_derive/
lib.rs

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
#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]

use proc_macro::TokenStream;

mod main_impl;

#[proc_macro]
pub fn main(input: TokenStream) -> TokenStream {
    handle_error(main_impl::impl_main(input.into()))
}

fn handle_error(input: Result<proc_macro2::TokenStream, syn::Error>) -> TokenStream {
    match input {
        Ok(value) => value.into(),
        Err(err) => err.to_compile_error().into(),
    }
}

#[cfg(test)]
#[cfg_attr(all(test, coverage_nightly), coverage(off))]
mod tests {
    use super::*;

    #[test]
    fn test_main() {
        let input = quote::quote! {
            MyGlobal {
                MyService,
            }
        };

        let output = match main_impl::impl_main(input) {
            Ok(value) => value,
            Err(err) => err.to_compile_error(),
        };

        let syntax_tree = prettyplease::unparse(&syn::parse_file(&output.to_string()).unwrap());

        insta::assert_snapshot!(syntax_tree, @r##"
        #[automatically_derived]
        fn main() -> ::scuffle_bootstrap::prelude::anyhow::Result<()> {
            #[doc(hidden)]
            pub const fn impl_global<G: ::scuffle_bootstrap::global::Global>() {}
            const _: () = impl_global::<MyGlobal>();
            ::scuffle_bootstrap::prelude::anyhow::Context::context(
                <MyGlobal as ::scuffle_bootstrap::global::Global>::pre_init(),
                "pre_init",
            )?;
            let runtime = <MyGlobal as ::scuffle_bootstrap::global::Global>::tokio_runtime();
            let config = ::scuffle_bootstrap::prelude::anyhow::Context::context(
                runtime
                    .block_on(
                        <<MyGlobal as ::scuffle_bootstrap::global::Global>::Config as ::scuffle_bootstrap::config::ConfigParser>::parse(),
                    ),
                "config parse",
            )?;
            let ctx_handle = ::scuffle_bootstrap::prelude::scuffle_context::Handler::global();
            let mut shared_global = ::core::option::Option::None;
            let mut services_vec = ::std::vec::Vec::<
                ::scuffle_bootstrap::service::NamedFuture<
                    ::scuffle_bootstrap::prelude::tokio::task::JoinHandle<anyhow::Result<()>>,
                >,
            >::new();
            let result = runtime
                .block_on(async {
                    let global = <MyGlobal as ::scuffle_bootstrap::global::Global>::init(config)
                        .await?;
                    shared_global = ::core::option::Option::Some(global.clone());
                    {
                        #[doc(hidden)]
                        pub async fn spawn_service(
                            svc: impl ::scuffle_bootstrap::service::Service<MyGlobal>,
                            global: &::std::sync::Arc<MyGlobal>,
                            ctx_handle: &::scuffle_bootstrap::prelude::scuffle_context::Handler,
                            name: &'static str,
                        ) -> anyhow::Result<
                            Option<
                                ::scuffle_bootstrap::service::NamedFuture<
                                    ::scuffle_bootstrap::prelude::tokio::task::JoinHandle<
                                        anyhow::Result<()>,
                                    >,
                                >,
                            >,
                        > {
                            let name = ::scuffle_bootstrap::service::Service::<
                                MyGlobal,
                            >::name(&svc)
                                .unwrap_or_else(|| name);
                            if ::scuffle_bootstrap::prelude::anyhow::Context::context(
                                ::scuffle_bootstrap::service::Service::<
                                    MyGlobal,
                                >::enabled(&svc, &global)
                                    .await,
                                name,
                            )? {
                                Ok(
                                    Some(
                                        ::scuffle_bootstrap::service::NamedFuture::new(
                                            name,
                                            ::scuffle_bootstrap::prelude::tokio::spawn(
                                                ::scuffle_bootstrap::service::Service::<
                                                    MyGlobal,
                                                >::run(svc, global.clone(), ctx_handle.context()),
                                            ),
                                        ),
                                    ),
                                )
                            } else {
                                Ok(None)
                            }
                        }
                        let res = spawn_service(MyService, &global, &ctx_handle, "MyService")
                            .await;
                        if let Some(spawned) = res? {
                            services_vec.push(spawned);
                        }
                    }
                    <MyGlobal as ::scuffle_bootstrap::global::Global>::on_services_start(&global)
                        .await?;
                    macro_rules! handle_service_exit {
                        ($remaining:ident) => {
                            { let ((name, result), _, remaining) =
                            ::scuffle_bootstrap::prelude::futures::future::select_all($remaining)
                            . await; let result =
                            ::scuffle_bootstrap::prelude::anyhow::Context::context(::scuffle_bootstrap::prelude::anyhow::Context::context(result,
                            name) ?, name); < MyGlobal as ::scuffle_bootstrap::global::Global >
                            ::on_service_exit(& global, name, result). await ?; remaining }
                        };
                    }
                    let mut remaining = handle_service_exit!(services_vec);
                    while !remaining.is_empty() {
                        remaining = handle_service_exit!(remaining);
                    }
                    ::scuffle_bootstrap::prelude::anyhow::Ok(())
                });
            let ::core::option::Option::Some(global) = shared_global else {
                return result;
            };
            runtime
                .block_on(
                    <MyGlobal as ::scuffle_bootstrap::global::Global>::on_exit(&global, result),
                )
        }
        "##);
    }
}