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