Skip to main content

timer_lib/
errors.rs

1//! Error handling module for TimerLib.
2
3use std::backtrace::Backtrace;
4use std::error::Error as StdError;
5use std::fmt::{Display, Formatter};
6use std::sync::Arc;
7
8/// Represents timer-related failures.
9#[derive(Debug, Clone)]
10pub struct TimerError {
11    kind: TimerErrorKind,
12    backtrace: Arc<Backtrace>,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16enum TimerErrorKind {
17    InvalidParameter(String),
18    NotRunning,
19    NotPaused,
20    ReentrantOperation(String),
21    CallbackFailed(String),
22}
23
24impl TimerError {
25    /// Creates an invalid parameter error.
26    pub fn invalid_parameter(message: impl Into<String>) -> Self {
27        Self::new(TimerErrorKind::InvalidParameter(message.into()))
28    }
29
30    /// Creates an error for operations that require a running timer.
31    pub fn not_running() -> Self {
32        Self::new(TimerErrorKind::NotRunning)
33    }
34
35    /// Creates an error for operations that require a paused timer.
36    pub fn not_paused() -> Self {
37        Self::new(TimerErrorKind::NotPaused)
38    }
39
40    /// Creates an error for operations that cannot safely run from the active callback.
41    pub fn reentrant_operation(message: impl Into<String>) -> Self {
42        Self::new(TimerErrorKind::ReentrantOperation(message.into()))
43    }
44
45    /// Creates an error for callback execution failures.
46    pub fn callback_failed(message: impl Into<String>) -> Self {
47        Self::new(TimerErrorKind::CallbackFailed(message.into()))
48    }
49
50    /// Returns true when the error is an invalid parameter error.
51    pub fn is_invalid_parameter(&self) -> bool {
52        matches!(self.kind, TimerErrorKind::InvalidParameter(_))
53    }
54
55    /// Returns the invalid parameter message when available.
56    pub fn invalid_parameter_message(&self) -> Option<&str> {
57        match &self.kind {
58            TimerErrorKind::InvalidParameter(message) => Some(message.as_str()),
59            _ => None,
60        }
61    }
62
63    /// Returns true when the error indicates the timer was not running.
64    pub fn is_not_running(&self) -> bool {
65        matches!(self.kind, TimerErrorKind::NotRunning)
66    }
67
68    /// Returns true when the error indicates the timer was not paused.
69    pub fn is_not_paused(&self) -> bool {
70        matches!(self.kind, TimerErrorKind::NotPaused)
71    }
72
73    /// Returns true when the error indicates a reentrant callback operation.
74    pub fn is_reentrant_operation(&self) -> bool {
75        matches!(self.kind, TimerErrorKind::ReentrantOperation(_))
76    }
77
78    /// Returns the reentrant-operation message when available.
79    pub fn reentrant_operation_message(&self) -> Option<&str> {
80        match &self.kind {
81            TimerErrorKind::ReentrantOperation(message) => Some(message.as_str()),
82            _ => None,
83        }
84    }
85
86    /// Returns true when the error indicates callback execution failed.
87    pub fn is_callback_failed(&self) -> bool {
88        matches!(self.kind, TimerErrorKind::CallbackFailed(_))
89    }
90
91    /// Returns the callback failure message when available.
92    pub fn callback_failure_message(&self) -> Option<&str> {
93        match &self.kind {
94            TimerErrorKind::CallbackFailed(message) => Some(message.as_str()),
95            _ => None,
96        }
97    }
98
99    /// Returns the captured backtrace.
100    pub fn backtrace(&self) -> &Backtrace {
101        self.backtrace.as_ref()
102    }
103
104    fn new(kind: TimerErrorKind) -> Self {
105        Self {
106            kind,
107            backtrace: Arc::new(Backtrace::capture()),
108        }
109    }
110}
111
112impl Display for TimerError {
113    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
114        match &self.kind {
115            TimerErrorKind::InvalidParameter(message) => {
116                write!(f, "Invalid parameter: {message}")
117            }
118            TimerErrorKind::NotRunning => write!(f, "Operation requires a running timer."),
119            TimerErrorKind::NotPaused => write!(f, "Operation requires a paused timer."),
120            TimerErrorKind::ReentrantOperation(message) => {
121                write!(f, "Reentrant timer operation is not allowed: {message}")
122            }
123            TimerErrorKind::CallbackFailed(message) => {
124                write!(f, "Callback execution failed: {message}")
125            }
126        }
127    }
128}
129
130impl StdError for TimerError {}
131
132impl PartialEq for TimerError {
133    fn eq(&self, other: &Self) -> bool {
134        self.kind == other.kind
135    }
136}
137
138impl Eq for TimerError {}
139
140// Rust guideline compliant 2026-02-21
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn error_messages_are_stable_and_descriptive() {
148        assert_eq!(
149            TimerError::invalid_parameter("bad interval").to_string(),
150            "Invalid parameter: bad interval"
151        );
152        assert_eq!(
153            TimerError::not_running().to_string(),
154            "Operation requires a running timer."
155        );
156        assert_eq!(
157            TimerError::not_paused().to_string(),
158            "Operation requires a paused timer."
159        );
160        assert_eq!(
161            TimerError::reentrant_operation("stop() from callback").to_string(),
162            "Reentrant timer operation is not allowed: stop() from callback"
163        );
164        assert_eq!(
165            TimerError::callback_failed("boom").to_string(),
166            "Callback execution failed: boom"
167        );
168    }
169
170    #[test]
171    fn error_helpers_expose_stable_queries() {
172        let invalid = TimerError::invalid_parameter("interval");
173        assert!(invalid.is_invalid_parameter());
174        assert_eq!(invalid.invalid_parameter_message(), Some("interval"));
175        assert!(!invalid.is_not_running());
176
177        let callback = TimerError::callback_failed("boom");
178        assert!(callback.is_callback_failed());
179        assert_eq!(callback.callback_failure_message(), Some("boom"));
180
181        let reentrant = TimerError::reentrant_operation("join()");
182        assert!(reentrant.is_reentrant_operation());
183        assert_eq!(reentrant.reentrant_operation_message(), Some("join()"));
184        assert!(
185            callback.backtrace().status() != std::backtrace::BacktraceStatus::Disabled
186                || callback.backtrace().status() == std::backtrace::BacktraceStatus::Disabled
187        );
188    }
189}