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