mecomp_tui/
termination.rs1#[cfg(unix)]
2use tokio::signal::unix::signal;
3use tokio::sync::broadcast;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum Interrupted {
7 OsSigInt,
8 OsSigQuit,
9 OsSigTerm,
10 UserInt,
11}
12
13#[derive(Debug, Clone)]
14pub struct Terminator {
15 interrupt_tx: broadcast::Sender<Interrupted>,
16}
17
18impl Terminator {
19 #[must_use]
20 pub const fn new(interrupt_tx: broadcast::Sender<Interrupted>) -> Self {
21 Self { interrupt_tx }
22 }
23
24 pub fn terminate(&mut self, interrupted: Interrupted) -> anyhow::Result<()> {
30 self.interrupt_tx.send(interrupted)?;
31
32 Ok(())
33 }
34}
35
36#[cfg(unix)]
37async fn terminate_by_unix_signal(mut terminator: Terminator) {
38 let mut interrupt_signal = signal(tokio::signal::unix::SignalKind::interrupt())
39 .expect("failed to create interrupt signal stream");
40 let mut term_signal = signal(tokio::signal::unix::SignalKind::terminate())
41 .expect("failed to create terminate signal stream");
42 let mut quit_signal = signal(tokio::signal::unix::SignalKind::quit())
43 .expect("failed to create quit signal stream");
44
45 tokio::select! {
46 _ = interrupt_signal.recv() => {
47 terminator
48 .terminate(Interrupted::OsSigInt)
49 .expect("failed to send interrupt signal");
50 }
51 _ = term_signal.recv() => {
52 terminator
53 .terminate(Interrupted::OsSigTerm)
54 .expect("failed to send terminate signal");
55 }
56 _ = quit_signal.recv() => {
57 terminator
58 .terminate(Interrupted::OsSigQuit)
59 .expect("failed to send quit signal");
60 }
61 }
62}
63
64#[allow(clippy::module_name_repetitions)]
66#[must_use]
67pub fn create_termination() -> (Terminator, broadcast::Receiver<Interrupted>) {
68 let (tx, rx) = broadcast::channel(1);
69 let terminator = Terminator::new(tx);
70
71 #[cfg(unix)]
72 tokio::spawn(terminate_by_unix_signal(terminator.clone()));
73
74 (terminator, rx)
75}
76
77#[cfg(test)]
78mod test {
79 use std::time::Duration;
80
81 use super::*;
82 use pretty_assertions::assert_eq;
83 use rstest::rstest;
84
85 #[rstest]
86 #[timeout(Duration::from_secs(1))]
87 #[tokio::test]
88 async fn test_terminate() {
89 let (mut terminator, mut rx) = create_termination();
90
91 terminator
92 .terminate(Interrupted::UserInt)
93 .expect("failed to send interrupt signal");
94
95 assert_eq!(rx.recv().await, Ok(Interrupted::UserInt));
96 }
97}