scuffle_bootstrap_derive/
lib.rs1#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]
2
3use proc_macro::TokenStream;
4
5mod main_impl;
6
7#[proc_macro]
8pub fn main(input: TokenStream) -> TokenStream {
9 handle_error(main_impl::impl_main(input.into()))
10}
11
12fn handle_error(input: Result<proc_macro2::TokenStream, syn::Error>) -> TokenStream {
13 match input {
14 Ok(value) => value.into(),
15 Err(err) => err.to_compile_error().into(),
16 }
17}
18
19#[cfg(test)]
20#[cfg_attr(all(test, coverage_nightly), coverage(off))]
21mod tests {
22 use super::*;
23
24 #[test]
25 fn test_main() {
26 let input = quote::quote! {
27 MyGlobal {
28 MyService,
29 }
30 };
31
32 let output = match main_impl::impl_main(input) {
33 Ok(value) => value,
34 Err(err) => err.to_compile_error(),
35 };
36
37 let syntax_tree = prettyplease::unparse(&syn::parse_file(&output.to_string()).unwrap());
38
39 insta::assert_snapshot!(syntax_tree, @r##"
40 #[automatically_derived]
41 fn main() -> ::scuffle_bootstrap::prelude::anyhow::Result<()> {
42 #[doc(hidden)]
43 pub const fn impl_global<G: ::scuffle_bootstrap::global::Global>() {}
44 const _: () = impl_global::<MyGlobal>();
45 ::scuffle_bootstrap::prelude::anyhow::Context::context(
46 <MyGlobal as ::scuffle_bootstrap::global::Global>::pre_init(),
47 "pre_init",
48 )?;
49 let runtime = <MyGlobal as ::scuffle_bootstrap::global::Global>::tokio_runtime();
50 let config = ::scuffle_bootstrap::prelude::anyhow::Context::context(
51 runtime
52 .block_on(
53 <<MyGlobal as ::scuffle_bootstrap::global::Global>::Config as ::scuffle_bootstrap::config::ConfigParser>::parse(),
54 ),
55 "config parse",
56 )?;
57 let ctx_handle = ::scuffle_bootstrap::prelude::scuffle_context::Handler::global();
58 let mut shared_global = ::core::option::Option::None;
59 let mut services_vec = ::std::vec::Vec::<
60 ::scuffle_bootstrap::service::NamedFuture<
61 ::scuffle_bootstrap::prelude::tokio::task::JoinHandle<anyhow::Result<()>>,
62 >,
63 >::new();
64 let result = runtime
65 .block_on(async {
66 let global = <MyGlobal as ::scuffle_bootstrap::global::Global>::init(config)
67 .await?;
68 shared_global = ::core::option::Option::Some(global.clone());
69 {
70 #[doc(hidden)]
71 pub async fn spawn_service(
72 svc: impl ::scuffle_bootstrap::service::Service<MyGlobal>,
73 global: &::std::sync::Arc<MyGlobal>,
74 ctx_handle: &::scuffle_bootstrap::prelude::scuffle_context::Handler,
75 name: &'static str,
76 ) -> anyhow::Result<
77 Option<
78 ::scuffle_bootstrap::service::NamedFuture<
79 ::scuffle_bootstrap::prelude::tokio::task::JoinHandle<
80 anyhow::Result<()>,
81 >,
82 >,
83 >,
84 > {
85 let name = ::scuffle_bootstrap::service::Service::<
86 MyGlobal,
87 >::name(&svc)
88 .unwrap_or_else(|| name);
89 if ::scuffle_bootstrap::prelude::anyhow::Context::context(
90 ::scuffle_bootstrap::service::Service::<
91 MyGlobal,
92 >::enabled(&svc, &global)
93 .await,
94 name,
95 )? {
96 Ok(
97 Some(
98 ::scuffle_bootstrap::service::NamedFuture::new(
99 name,
100 ::scuffle_bootstrap::prelude::tokio::spawn(
101 ::scuffle_bootstrap::service::Service::<
102 MyGlobal,
103 >::run(svc, global.clone(), ctx_handle.context()),
104 ),
105 ),
106 ),
107 )
108 } else {
109 Ok(None)
110 }
111 }
112 let res = spawn_service(MyService, &global, &ctx_handle, "MyService")
113 .await;
114 if let Some(spawned) = res? {
115 services_vec.push(spawned);
116 }
117 }
118 <MyGlobal as ::scuffle_bootstrap::global::Global>::on_services_start(&global)
119 .await?;
120 macro_rules! handle_service_exit {
121 ($remaining:ident) => {
122 { let ((name, result), _, remaining) =
123 ::scuffle_bootstrap::prelude::futures::future::select_all($remaining)
124 . await; let result =
125 ::scuffle_bootstrap::prelude::anyhow::Context::context(::scuffle_bootstrap::prelude::anyhow::Context::context(result,
126 name) ?, name); < MyGlobal as ::scuffle_bootstrap::global::Global >
127 ::on_service_exit(& global, name, result). await ?; remaining }
128 };
129 }
130 let mut remaining = handle_service_exit!(services_vec);
131 while !remaining.is_empty() {
132 remaining = handle_service_exit!(remaining);
133 }
134 ::scuffle_bootstrap::prelude::anyhow::Ok(())
135 });
136 let ::core::option::Option::Some(global) = shared_global else {
137 return result;
138 };
139 runtime
140 .block_on(
141 <MyGlobal as ::scuffle_bootstrap::global::Global>::on_exit(&global, result),
142 )
143 }
144 "##);
145 }
146}