jellytau_lib/player/
sleep_timer.rs1use serde::{Deserialize, Serialize};
2
3#[derive(specta::Type, Debug, Clone, Serialize, Deserialize, PartialEq)]
6#[serde(tag = "kind", rename_all = "camelCase")]
7pub enum SleepTimerMode {
8 Off,
10 Time {
12 #[serde(rename = "endTime")]
13 end_time: i64, },
15 EndOfTrack,
17 Episodes { remaining: u32 },
19}
20
21#[derive(specta::Type, Debug, Clone, Serialize, Deserialize)]
23#[serde(rename_all = "camelCase")]
24pub struct SleepTimerState {
25 pub mode: SleepTimerMode,
26 pub remaining_seconds: u32,
27}
28
29impl Default for SleepTimerState {
30 fn default() -> Self {
31 Self {
32 mode: SleepTimerMode::Off,
33 remaining_seconds: 0,
34 }
35 }
36}
37
38impl SleepTimerState {
39 pub fn is_active(&self) -> bool {
41 !matches!(self.mode, SleepTimerMode::Off)
42 }
43
44 pub fn update_remaining_seconds(&mut self) {
46 if let SleepTimerMode::Time { end_time } = self.mode {
47 let now = chrono::Utc::now().timestamp_millis();
48 self.remaining_seconds = if now >= end_time {
49 0
50 } else {
51 ((end_time - now) / 1000).max(0) as u32
52 };
53 }
54 }
55
56 pub fn decrement_episode(&mut self) -> bool {
59 match &mut self.mode {
60 SleepTimerMode::Episodes { remaining } => {
61 if *remaining <= 1 {
62 self.mode = SleepTimerMode::Off;
63 self.remaining_seconds = 0;
64 true
65 } else {
66 *remaining -= 1;
67 false
68 }
69 }
70 _ => false,
71 }
72 }
73
74 pub fn cancel(&mut self) {
76 self.mode = SleepTimerMode::Off;
77 self.remaining_seconds = 0;
78 }
79}
80
81#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn test_sleep_timer_episode_decrement() {
88 let mut timer = SleepTimerState {
89 mode: SleepTimerMode::Episodes { remaining: 2 },
90 remaining_seconds: 0,
91 };
92
93 assert!(!timer.decrement_episode());
94 assert!(matches!(
95 timer.mode,
96 SleepTimerMode::Episodes { remaining: 1 }
97 ));
98
99 assert!(timer.decrement_episode());
100 assert!(matches!(timer.mode, SleepTimerMode::Off));
101 }
102
103 #[test]
104 fn test_sleep_timer_is_active() {
105 let timer = SleepTimerState::default();
106 assert!(!timer.is_active());
107
108 let timer = SleepTimerState {
109 mode: SleepTimerMode::Time { end_time: 0 },
110 remaining_seconds: 0,
111 };
112 assert!(timer.is_active());
113
114 let timer = SleepTimerState {
115 mode: SleepTimerMode::EndOfTrack,
116 remaining_seconds: 0,
117 };
118 assert!(timer.is_active());
119
120 let timer = SleepTimerState {
121 mode: SleepTimerMode::Episodes { remaining: 3 },
122 remaining_seconds: 0,
123 };
124 assert!(timer.is_active());
125 }
126
127 #[test]
128 fn test_sleep_timer_cancel() {
129 let mut timer = SleepTimerState {
130 mode: SleepTimerMode::Episodes { remaining: 5 },
131 remaining_seconds: 300,
132 };
133
134 timer.cancel();
135 assert!(matches!(timer.mode, SleepTimerMode::Off));
136 assert_eq!(timer.remaining_seconds, 0);
137 }
138
139 #[test]
140 fn test_update_remaining_seconds() {
141 let end_time = chrono::Utc::now().timestamp_millis() + 30000; let mut timer = SleepTimerState {
143 mode: SleepTimerMode::Time { end_time },
144 remaining_seconds: 0,
145 };
146
147 timer.update_remaining_seconds();
148 assert!(timer.remaining_seconds >= 29 && timer.remaining_seconds <= 31);
150 }
151}