qubit_clock/error/time_error.rs
1// =============================================================================
2// Copyright (c) 2025 - 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Defines errors produced by time-domain operations.
9
10use std::time::Duration;
11
12use thiserror::Error;
13
14use super::TimerUnavailableError;
15use crate::ClockDomain;
16
17/// Describes an invalid monotonic-time operation.
18///
19/// Callers must include a wildcard arm when matching this enum so future
20/// versions can add errors without breaking downstream code:
21///
22/// ```
23/// use qubit_clock::TimeError;
24///
25/// fn message(error: TimeError) -> &'static str {
26/// match error {
27/// TimeError::ClockDomainMismatch { .. } => "domain mismatch",
28/// TimeError::InstantOverflow => "overflow",
29/// TimeError::CannotMoveBackward { .. } => "backward move",
30/// TimeError::InvalidInstantOrder { .. } => "invalid order",
31/// TimeError::TimerUnavailable { .. } => "timer unavailable",
32/// _ => "other time error",
33/// }
34/// }
35///
36/// assert_eq!("overflow", message(TimeError::InstantOverflow));
37/// ```
38///
39/// An exhaustive match is rejected outside this crate because additional
40/// variants may be introduced in a compatible release:
41///
42/// ```compile_fail
43/// use qubit_clock::TimeError;
44///
45/// fn exhaustive_message(error: TimeError) -> &'static str {
46/// match error {
47/// TimeError::ClockDomainMismatch { .. } => "domain mismatch",
48/// TimeError::InstantOverflow => "overflow",
49/// TimeError::CannotMoveBackward { .. } => "backward move",
50/// TimeError::InvalidInstantOrder { .. } => "invalid order",
51/// TimeError::TimerUnavailable { .. } => "timer unavailable",
52/// }
53/// }
54/// ```
55#[non_exhaustive]
56#[derive(Debug, Error)]
57pub enum TimeError {
58 /// Two monotonic instants belong to different clock domains.
59 #[error("monotonic clock domain mismatch: expected {expected}, actual {actual}")]
60 ClockDomainMismatch {
61 /// Domain required by the receiving clock or instant.
62 expected: ClockDomain,
63 /// Domain carried by the supplied instant.
64 actual: ClockDomain,
65 },
66 /// A monotonic instant cannot represent the requested result.
67 #[error("monotonic instant overflow")]
68 InstantOverflow,
69 /// A manual monotonic clock was asked to move backward.
70 #[error(
71 "manual monotonic time cannot move backward from {current_elapsed:?} to \
72 {requested_elapsed:?}"
73 )]
74 CannotMoveBackward {
75 /// Current elapsed duration in the manual clock domain.
76 current_elapsed: Duration,
77 /// Earlier elapsed duration requested by the caller.
78 requested_elapsed: Duration,
79 },
80 /// Duration was requested with an earlier instant after the current one.
81 #[error(
82 "instant at {earlier_elapsed:?} cannot be earlier than current instant \
83 at {current_elapsed:?}"
84 )]
85 InvalidInstantOrder {
86 /// Elapsed duration carried by the receiving current instant.
87 current_elapsed: Duration,
88 /// Elapsed duration carried by the supplied earlier instant.
89 earlier_elapsed: Duration,
90 },
91 /// A timer could not register or complete a requested deadline.
92 #[error("monotonic timer is unavailable: {source}")]
93 TimerUnavailable {
94 /// Backend error that prevented timer registration or completion.
95 #[source]
96 source: TimerUnavailableError,
97 },
98}
99
100impl From<TimerUnavailableError> for TimeError {
101 /// Wraps a timer-backend failure as a monotonic time error.
102 ///
103 /// # Parameters
104 ///
105 /// * `source` - Timer-backend failure to wrap.
106 ///
107 /// # Returns
108 ///
109 /// A monotonic time error retaining `source`.
110 #[inline(always)]
111 fn from(source: TimerUnavailableError) -> Self {
112 Self::TimerUnavailable { source }
113 }
114}