timer_lib/
timer.rs

1use async_trait::async_trait;
2use std::sync::Arc;
3use std::time::{Duration, Instant};
4use tokio::sync::Mutex;
5use tokio::sync::Notify;
6use tokio::time;
7
8#[cfg(feature = "logging")]
9use log::{debug, error};
10
11use crate::errors::TimerError;
12
13/// Represents the state of a timer.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum TimerState {
16    Running,
17    Paused,
18    Stopped,
19}
20
21/// Statistics for a timer.
22#[derive(Debug, Clone, Default)]
23pub struct TimerStatistics {
24    /// Number of times the callback has been executed.
25    pub execution_count: usize,
26    /// Total elapsed time since the timer started.
27    pub elapsed_time: Duration,
28}
29
30/// A trait for timer callbacks.
31#[async_trait]
32pub trait TimerCallback: Send + Sync {
33    /// The function to execute when the timer triggers.
34    async fn execute(&self) -> Result<(), TimerError>;
35}
36
37#[derive(Clone)]
38/// Timer struct for managing one-time and recurring tasks.
39pub struct Timer {
40    state: Arc<Mutex<TimerState>>,
41    handle: Option<Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>>,
42    interval: Duration,
43    expiration_count: Option<usize>,
44    statistics: Arc<Mutex<TimerStatistics>>,
45    pause_notify: Arc<Notify>,
46}
47
48impl Timer {
49    /// Creates a new timer.
50    pub fn new() -> Self {
51        Timer {
52            state: Arc::new(Mutex::new(TimerState::Stopped)),
53            handle: None,
54            interval: Duration::from_secs(0),
55            expiration_count: None,
56            statistics: Arc::new(Mutex::new(TimerStatistics::default())),
57            pause_notify: Arc::new(Notify::new()),
58        }
59    }
60
61    /// Starts a one-time timer.
62    pub async fn start_once<F>(&mut self, delay: Duration, callback: F) -> Result<(), TimerError>
63    where
64        F: TimerCallback + 'static,
65    {
66        self.start_internal(delay, callback, false, None).await
67    }
68
69    /// Starts a recurring timer with an optional expiration count.
70    pub async fn start_recurring<F>(
71        &mut self,
72        interval: Duration,
73        callback: F,
74        expiration_count: Option<usize>,
75    ) -> Result<(), TimerError>
76    where
77        F: TimerCallback + 'static,
78    {
79        self.start_internal(interval, callback, true, expiration_count)
80            .await
81    }
82
83    /// Pauses a running timer.
84    pub async fn pause(&self) -> Result<(), TimerError> {
85        let mut state = self.state.lock().await;
86        if *state == TimerState::Running {
87            *state = TimerState::Paused;
88            #[cfg(feature = "logging")]
89            debug!("Timer paused.");
90            Ok(())
91        } else {
92            Err(TimerError::TimerStopped)
93        }
94    }
95
96    /// Resumes a paused timer.
97    pub async fn resume(&self) -> Result<(), TimerError> {
98        let mut state = self.state.lock().await;
99        if *state == TimerState::Paused {
100            *state = TimerState::Running;
101            self.pause_notify.notify_one();
102            #[cfg(feature = "logging")]
103            debug!("Timer resumed.");
104            Ok(())
105        } else {
106            Err(TimerError::InvalidParameter("Timer is not paused.".into()))
107        }
108    }
109
110    /// Stops the timer.
111    pub async fn stop(&mut self) -> Result<(), TimerError> {
112        let mut state = self.state.lock().await;
113        if *state != TimerState::Stopped {
114            *state = TimerState::Stopped;
115            if let Some(handle) = self.handle.take() {
116                drop(state); // Release the lock before awaiting
117                #[cfg(feature = "logging")]
118                debug!("Stopping timer.");
119                if let Some(handle) = handle.lock().await.take() {
120                    handle.abort();
121                }
122            }
123            Ok(())
124        } else {
125            Err(TimerError::TimerStopped)
126        }
127    }
128
129    /// Adjusts the interval of a running timer.
130    pub fn adjust_interval(&mut self, new_interval: Duration) -> Result<(), TimerError> {
131        if new_interval.as_nanos() == 0 {
132            return Err(TimerError::InvalidParameter(
133                "Interval must be greater than zero.".into(),
134            ));
135        }
136        self.interval = new_interval;
137        #[cfg(feature = "logging")]
138        debug!("Timer interval adjusted.");
139        Ok(())
140    }
141
142    /// Gets the timer's statistics.
143    pub async fn get_statistics(&self) -> TimerStatistics {
144        self.statistics.lock().await.clone()
145    }
146
147    /// Gets the current state of the timer.
148    pub async fn get_state(&self) -> TimerState {
149        *self.state.lock().await
150    }
151
152    // Gets expiration count
153    pub fn get_expiration_count(&self) -> Option<usize> {
154        self.expiration_count
155    }
156
157    /// Internal method to start a timer.
158    /// Internal method to start a timer.
159    async fn start_internal<F>(
160        &mut self,
161        interval: Duration,
162        callback: F,
163        recurring: bool,
164        expiration_count: Option<usize>,
165    ) -> Result<(), TimerError>
166    where
167        F: TimerCallback + 'static,
168    {
169        if interval.as_nanos() == 0 {
170            return Err(TimerError::InvalidParameter(
171                "Interval must be greater than zero.".into(),
172            ));
173        }
174
175        if let Err(_e) = self.stop().await {
176            #[cfg(feature = "logging")]
177            error!("Failed to stop existing timer: {}", e);
178        }
179
180        // Set the timer state to Running
181        {
182            let mut state_lock = self.state.lock().await;
183            *state_lock = TimerState::Running;
184        }
185
186        // Clone variables for the async move closure
187        let state = Arc::clone(&self.state);
188        let statistics = Arc::clone(&self.statistics);
189        let pause_notify = Arc::clone(&self.pause_notify);
190        let interval = interval;
191        let expiration_count = expiration_count;
192        let callback = Arc::new(callback); // Wrap the callback in Arc
193
194        #[cfg(feature = "logging")]
195        debug!("Starting timer.");
196
197        self.handle = Some(Arc::new(Mutex::new(Some(tokio::spawn(async move {
198            let mut tick_count = 0;
199            let start_time = Instant::now();
200
201            loop {
202                // Check the timer state
203                let current_state = {
204                    let state_lock = state.lock().await;
205                    *state_lock
206                };
207
208                if current_state == TimerState::Stopped {
209                    break;
210                } else if current_state == TimerState::Paused {
211                    pause_notify.notified().await;
212                    continue;
213                }
214
215                // Wait for the interval
216                time::sleep(interval).await;
217
218                // Execute the callback
219                if let Err(_e) = callback.execute().await {
220                    #[cfg(feature = "logging")]
221                    error!("Callback execution error: {}", e);
222                }
223
224                // Update statistics
225                {
226                    let mut stats = statistics.lock().await;
227                    stats.execution_count += 1;
228                    stats.elapsed_time = start_time.elapsed();
229                }
230                tick_count += 1;
231
232                // Check expiration count
233                if let Some(max_ticks) = expiration_count {
234                    if tick_count >= max_ticks {
235                        #[cfg(feature = "logging")]
236                        debug!("Timer reached expiration count.");
237                        break;
238                    }
239                }
240
241                if !recurring {
242                    break;
243                }
244            }
245
246            #[cfg(feature = "logging")]
247            debug!("Timer stopped.");
248        })))));
249
250        Ok(())
251    }
252}
253
254unsafe impl Send for Timer {}
255unsafe impl Sync for Timer {}