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 super::TimerUnavailableError;
11use crate::ClockDomain;
12use std::time::Duration;
13use thiserror::Error;
14
15/// Describes an invalid monotonic-time operation.
16///
17/// Callers must include a wildcard arm when matching this enum so future
18/// versions can add errors without breaking downstream code:
19///
20/// ```
21/// use qubit_clock::TimeError;
22///
23/// fn message(error: TimeError) -> &'static str {
24/// match error {
25/// TimeError::ClockDomainMismatch { .. } => "domain mismatch",
26/// TimeError::InstantOverflow => "overflow",
27/// TimeError::CannotMoveBackward { .. } => "backward move",
28/// TimeError::InvalidInstantOrder { .. } => "invalid order",
29/// TimeError::TimerUnavailable { .. } => "timer unavailable",
30/// _ => "other time error",
31/// }
32/// }
33///
34/// assert_eq!("overflow", message(TimeError::InstantOverflow));
35/// ```
36///
37/// An exhaustive match is rejected outside this crate because additional
38/// variants may be introduced in a compatible release:
39///
40/// ```compile_fail
41/// use qubit_clock::TimeError;
42///
43/// fn exhaustive_message(error: TimeError) -> &'static str {
44/// match error {
45/// TimeError::ClockDomainMismatch { .. } => "domain mismatch",
46/// TimeError::InstantOverflow => "overflow",
47/// TimeError::CannotMoveBackward { .. } => "backward move",
48/// TimeError::InvalidInstantOrder { .. } => "invalid order",
49/// TimeError::TimerUnavailable { .. } => "timer unavailable",
50/// }
51/// }
52/// ```
53#[non_exhaustive]
54#[derive(Debug, Error)]
55pub enum TimeError {
56 /// Two monotonic instants belong to different clock domains.
57 #[error(
58 "monotonic clock domain mismatch: expected {expected}, actual {actual}"
59 )]
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}