playa/core/
debounced_preloader.rs1use std::time::{Duration, Instant};
11use uuid::Uuid;
12
13#[derive(Debug, Clone)]
27pub struct DebouncedPreloader {
28 delay: Duration,
30 pending: Option<(Uuid, Instant)>,
32}
33
34impl Default for DebouncedPreloader {
35 fn default() -> Self {
36 Self {
37 delay: Duration::from_millis(500),
38 pending: None,
39 }
40 }
41}
42
43impl DebouncedPreloader {
44 pub fn new(delay_ms: u64) -> Self {
46 Self {
47 delay: Duration::from_millis(delay_ms),
48 pending: None,
49 }
50 }
51
52 pub fn set_delay(&mut self, delay_ms: u64) {
54 self.delay = Duration::from_millis(delay_ms);
55 }
56
57 pub fn delay_ms(&self) -> u64 {
59 self.delay.as_millis() as u64
60 }
61
62 pub fn schedule(&mut self, comp_uuid: Uuid) {
65 let trigger_at = Instant::now() + self.delay;
66 self.pending = Some((comp_uuid, trigger_at));
67 log::trace!(
68 "DebouncedPreloader: scheduled preload for {} in {}ms",
69 comp_uuid,
70 self.delay.as_millis()
71 );
72 }
73
74 pub fn cancel(&mut self) {
76 if self.pending.is_some() {
77 log::trace!("DebouncedPreloader: cancelled pending preload");
78 }
79 self.pending = None;
80 }
81
82 pub fn tick(&mut self) -> Option<Uuid> {
86 let Some((uuid, trigger_at)) = self.pending else {
87 return None;
88 };
89
90 if Instant::now() >= trigger_at {
91 self.pending = None;
92 log::trace!("DebouncedPreloader: triggering preload for {}", uuid);
93 Some(uuid)
94 } else {
95 None
96 }
97 }
98
99 pub fn is_pending(&self) -> bool {
101 self.pending.is_some()
102 }
103
104 pub fn pending_comp(&self) -> Option<Uuid> {
106 self.pending.map(|(uuid, _)| uuid)
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_immediate_no_trigger() {
116 let mut preloader = DebouncedPreloader::new(100);
117 let uuid = Uuid::new_v4();
118
119 preloader.schedule(uuid);
120 assert!(preloader.is_pending());
121
122 assert!(preloader.tick().is_none());
124 }
125
126 #[test]
127 fn test_trigger_after_delay() {
128 let mut preloader = DebouncedPreloader::new(10); let uuid = Uuid::new_v4();
130
131 preloader.schedule(uuid);
132 std::thread::sleep(Duration::from_millis(15));
133
134 assert_eq!(preloader.tick(), Some(uuid));
136 assert!(!preloader.is_pending());
137 }
138
139 #[test]
140 fn test_debounce_resets_timer() {
141 let mut preloader = DebouncedPreloader::new(50);
142 let uuid1 = Uuid::new_v4();
143 let uuid2 = Uuid::new_v4();
144
145 preloader.schedule(uuid1);
146 std::thread::sleep(Duration::from_millis(30));
147
148 preloader.schedule(uuid2);
150
151 assert!(preloader.tick().is_none());
153 assert_eq!(preloader.pending_comp(), Some(uuid2));
154 }
155}