trz_gateway_common/
handle.rs1use nameth::NamedEnumValues as _;
4use nameth::nameth;
5use tokio::sync::oneshot;
6use tracing::debug;
7use tracing::info;
8use tracing::warn;
9
10#[must_use]
14pub struct ServerHandle<R> {
15 name: String,
16 shutdown_tx: Option<oneshot::Sender<String>>,
17 terminated_rx: Option<oneshot::Receiver<R>>,
18}
19
20impl<R> ServerHandle<R> {
21 pub fn new(name: impl Into<String>) -> (impl Future<Output = ()>, oneshot::Sender<R>, Self) {
26 let (shutdown_tx, shutdown_rx) = oneshot::channel();
27 let shutdown_rx = async move {
28 match shutdown_rx.await {
29 Ok(message) => info!("Server shutdown: {message}"),
30 Err(oneshot::error::RecvError { .. }) => debug!("Server handle dropped!"),
31 }
32 };
33 let (terminated_tx, terminated_rx) = oneshot::channel();
34 let handle = Self {
35 name: name.into(),
36 shutdown_tx: Some(shutdown_tx),
37 terminated_rx: Some(terminated_rx),
38 };
39 (shutdown_rx, terminated_tx, handle)
40 }
41
42 pub async fn stop(mut self, reason: impl std::fmt::Display) -> Result<R, ServerStopError> {
44 self.shutdown_tx
45 .take()
46 .expect("shutdown_tx")
47 .send(format!("{reason}"))
48 .map_err(|_| ServerStopError::NotRunning)?;
49 self.stopped().await
50 }
51
52 pub async fn stopped(mut self) -> Result<R, ServerStopError> {
53 self.terminated_rx
54 .take()
55 .expect("terminated_rx")
56 .await
57 .map_err(|_| ServerStopError::ShutdownError)
58 }
59}
60
61impl<R> Drop for ServerHandle<R> {
62 fn drop(&mut self) {
63 if self.terminated_rx.is_some() && !std::thread::panicking() {
64 warn!("The server '{}' was not shutdown", self.name);
65 }
66 }
67}
68
69#[nameth]
70#[derive(thiserror::Error, Debug)]
71pub enum ServerStopError {
72 #[error("[{n}] The server was not running", n = self.name())]
73 NotRunning,
74
75 #[error("[{n}] The server did not fully shutdown", n = self.name())]
76 ShutdownError,
77}