scuffle_bootstrap/
service.rs1use std::pin::Pin;
4use std::sync::Arc;
5use std::task::{Context, Poll, ready};
6
7pub trait Service<Global>: Send + Sync + 'static + Sized {
18 fn name(&self) -> Option<&'static str> {
20 None
21 }
22
23 fn enabled(&self, global: &Arc<Global>) -> impl std::future::Future<Output = anyhow::Result<bool>> + Send {
26 let _ = global;
27 std::future::ready(Ok(true))
28 }
29
30 fn run(
46 self,
47 global: Arc<Global>,
48 ctx: scuffle_context::Context,
49 ) -> impl std::future::Future<Output = anyhow::Result<()>> + Send + 'static {
50 let _ = global;
51 async move {
52 ctx.done().await;
53 Ok(())
54 }
55 }
56}
57
58impl<G, F, Fut> Service<G> for F
59where
60 F: FnOnce(Arc<G>, scuffle_context::Context) -> Fut + Send + Sync + 'static,
61 Fut: std::future::Future<Output = anyhow::Result<()>> + Send + 'static,
62{
63 fn run(
64 self,
65 global: Arc<G>,
66 ctx: scuffle_context::Context,
67 ) -> impl std::future::Future<Output = anyhow::Result<()>> + Send + 'static {
68 self(global, ctx)
69 }
70}
71
72pin_project_lite::pin_project! {
73 #[must_use = "futures do nothing unless polled"]
75 pub struct NamedFuture<T> {
76 name: &'static str,
77 #[pin]
78 fut: T,
79 }
80}
81
82impl<T> NamedFuture<T> {
83 pub fn new(name: &'static str, fut: T) -> Self {
85 Self { name, fut }
86 }
87}
88
89impl<T> std::future::Future for NamedFuture<T>
90where
91 T: std::future::Future,
92{
93 type Output = (&'static str, T::Output);
94
95 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
96 let this = self.project();
97 let res = ready!(this.fut.poll(cx));
98 Poll::Ready((this.name, res))
99 }
100}
101
102#[cfg(test)]
103#[cfg_attr(all(test, coverage_nightly), coverage(off))]
104mod tests {
105 use std::sync::Arc;
106
107 use scuffle_future_ext::FutureExt;
108
109 use super::{NamedFuture, Service};
110
111 struct DefaultService;
112
113 impl Service<()> for DefaultService {}
114
115 #[tokio::test]
116 async fn defaukt_service() {
117 let svc = DefaultService;
118 let global = Arc::new(());
119 let (ctx, handler) = scuffle_context::Context::new();
120
121 assert_eq!(svc.name(), None);
122 assert!(svc.enabled(&global).await.unwrap());
123
124 handler.cancel();
125
126 assert!(matches!(svc.run(global, ctx).await, Ok(())));
127
128 assert!(
129 handler
130 .shutdown()
131 .with_timeout(tokio::time::Duration::from_millis(200))
132 .await
133 .is_ok()
134 );
135 }
136
137 #[tokio::test]
138 async fn future_service() {
139 let (ctx, handler) = scuffle_context::Context::new();
140 let global = Arc::new(());
141
142 let fut_fn = |_global: Arc<()>, _ctx: scuffle_context::Context| async { anyhow::Result::<()>::Ok(()) };
143 assert!(fut_fn.run(global, ctx).await.is_ok());
144
145 handler.cancel();
146 assert!(
147 handler
148 .shutdown()
149 .with_timeout(tokio::time::Duration::from_millis(200))
150 .await
151 .is_ok()
152 );
153 }
154
155 #[tokio::test]
156 async fn named_future() {
157 let named_fut = NamedFuture::new("test", async { 42 });
158 assert_eq!(named_fut.await, ("test", 42));
159 }
160}