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