1use std::sync::atomic::AtomicU8;
6
7use tokio::sync::OnceCell;
8
9use crate::{Payload, SoftCycleController, SoftCycleListener};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum SoftCycleMessage {
14 Shutdown,
15 Restart,
16}
17
18impl From<u8> for SoftCycleMessage {
26 fn from(value: u8) -> Self {
27 match value {
28 0 => Self::Shutdown,
29 1 => Self::Restart,
30 _ => panic!("Invalid soft cycle message: {}", value),
31 }
32 }
33}
34
35impl From<SoftCycleMessage> for u8 {
36 fn from(value: SoftCycleMessage) -> Self {
37 match value {
38 SoftCycleMessage::Shutdown => 0,
39 SoftCycleMessage::Restart => 1,
40 }
41 }
42}
43
44impl Payload for SoftCycleMessage {
45 type UnderlyingAtomic = AtomicU8;
46}
47
48static SHUTDOWN_CONTROLLER: OnceCell<SoftCycleController<SoftCycleMessage>> = OnceCell::const_new();
50
51pub async fn get_lifetime_controller() -> &'static SoftCycleController<SoftCycleMessage> {
53 SHUTDOWN_CONTROLLER
54 .get_or_init(|| async { SoftCycleController::new() })
55 .await
56}
57
58#[must_use = "Caller must check if the operation was successful"]
66pub async fn try_shutdown() -> bool {
67 get_lifetime_controller()
68 .await
69 .try_notify(SoftCycleMessage::Shutdown)
70 .is_ok()
71}
72
73#[must_use = "Caller must check if the operation was successful"]
81pub async fn try_restart() -> bool {
82 get_lifetime_controller()
83 .await
84 .try_notify(SoftCycleMessage::Restart)
85 .is_ok()
86}
87
88#[must_use = "Caller must await the listener to receive the signal"]
95pub async fn listener() -> SoftCycleListener<'static, SoftCycleMessage> {
96 get_lifetime_controller().await.listener()
97}
98
99pub async fn clear() {
105 let _ = get_lifetime_controller().await.try_clear();
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
115 fn conversion_u8_to_message_valid_zero_is_shutdown() {
116 let m: SoftCycleMessage = 0u8.into();
117 assert_eq!(m, SoftCycleMessage::Shutdown);
118 }
119
120 #[test]
121 fn conversion_u8_to_message_valid_one_is_restart() {
122 let m: SoftCycleMessage = 1u8.into();
123 assert_eq!(m, SoftCycleMessage::Restart);
124 }
125
126 #[test]
127 fn conversion_message_to_u8_roundtrip() {
128 assert_eq!(u8::from(SoftCycleMessage::Shutdown), 0);
129 assert_eq!(u8::from(SoftCycleMessage::Restart), 1);
130 assert_eq!(
131 SoftCycleMessage::from(u8::from(SoftCycleMessage::Shutdown)),
132 SoftCycleMessage::Shutdown
133 );
134 assert_eq!(
135 SoftCycleMessage::from(u8::from(SoftCycleMessage::Restart)),
136 SoftCycleMessage::Restart
137 );
138 }
139
140 #[test]
143 #[should_panic(expected = "Invalid soft cycle message")]
144 fn conversion_u8_to_message_invalid_panics_two() {
145 let _: SoftCycleMessage = 2u8.into();
146 }
147
148 #[test]
149 #[should_panic(expected = "Invalid soft cycle message")]
150 fn conversion_u8_to_message_invalid_panics_255() {
151 let _: SoftCycleMessage = 255u8.into();
152 }
153}