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