aimdb_core/time.rs
1//! Time and timing utilities for AimDB
2//!
3//! This module provides time-related traits and utilities for runtime adapters,
4//! including timestamping, delays, and scheduling operations.
5
6use core::future::Future;
7
8/// Trait for adapters that provide current time information
9///
10/// Enables timing information for timestamping, performance measurement, and scheduling.
11pub trait TimestampProvider {
12 /// Type representing an instant in time for this runtime
13 type Instant;
14
15 /// Gets the current timestamp according to the runtime's time source
16 ///
17 /// # Example
18 /// ```rust,no_run
19 /// use aimdb_core::time::TimestampProvider;
20 ///
21 /// fn log_operation<T: TimestampProvider>(provider: &T) {
22 /// let _timestamp = provider.now();
23 /// println!("Operation completed");
24 /// }
25 /// ```
26 fn now(&self) -> Self::Instant;
27}
28
29/// Trait for adapters that support sleep/delay operations
30///
31/// Provides capability to pause execution for a specified duration.
32pub trait SleepCapable {
33 /// Type representing a duration for this runtime
34 type Duration;
35
36 /// Pauses execution for the specified duration without blocking other tasks
37 ///
38 /// # Example
39 /// ```rust,no_run
40 /// use aimdb_core::time::SleepCapable;
41 /// use std::time::Duration;
42 ///
43 /// async fn rate_limited_operation<S: SleepCapable<Duration = Duration>>(
44 /// sleeper: &S
45 /// ) {
46 /// println!("Starting operation...");
47 /// sleeper.sleep(Duration::from_secs(1)).await;
48 /// println!("Operation completed after delay");
49 /// }
50 /// ```
51 fn sleep(&self, duration: Self::Duration) -> impl Future<Output = ()> + Send;
52}
53
54/// Utility functions for time-based operations
55pub mod utils {
56 use super::*;
57
58 /// Measures the execution time of an async operation
59 ///
60 /// Works in both `std` and `no_std` environments using the provided `TimestampProvider`.
61 pub async fn measure_async<F, T, P>(provider: &P, operation: F) -> (T, P::Instant, P::Instant)
62 where
63 F: Future<Output = T>,
64 P: TimestampProvider,
65 {
66 let start = provider.now();
67 let result = operation.await;
68 let end = provider.now();
69 (result, start, end)
70 }
71
72 /// Creates a generic timeout error for operations that exceed their time limit
73 pub fn create_timeout_error(_operation_name: &str) -> crate::DbError {
74 crate::DbError::ConnectionFailed {
75 #[cfg(feature = "std")]
76 endpoint: "timeout".to_string(),
77 #[cfg(feature = "std")]
78 reason: format!("{} operation timed out", _operation_name),
79 #[cfg(not(feature = "std"))]
80 _endpoint: (),
81 #[cfg(not(feature = "std"))]
82 _reason: (),
83 }
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[cfg(feature = "std")]
92 mod std_tests {
93 use super::*;
94 use std::time::Instant;
95
96 // Mock providers for testing
97 struct MockTimestampProvider;
98
99 impl TimestampProvider for MockTimestampProvider {
100 type Instant = Instant;
101
102 fn now(&self) -> Self::Instant {
103 Instant::now()
104 }
105 }
106
107 #[test]
108 fn test_timestamp_provider_trait() {
109 let provider = MockTimestampProvider;
110 let timestamp1 = provider.now();
111
112 // Timestamps should be valid Instant values
113 let timestamp2 = provider.now();
114
115 // Second timestamp should be greater than or equal to first
116 assert!(timestamp2 >= timestamp1);
117 }
118
119 #[test]
120 fn test_timeout_error_creation() {
121 let error = utils::create_timeout_error("database query");
122
123 // Test error format for std feature
124 #[cfg(feature = "std")]
125 {
126 let error_string = format!("{}", error);
127 assert!(error_string.contains("database query operation timed out"));
128 }
129 }
130
131 #[test]
132 fn test_measure_async_trait_based() {
133 let provider = MockTimestampProvider;
134
135 // Test that we can measure with the trait-based approach
136 let runtime = tokio::runtime::Runtime::new().unwrap();
137 let (result, start, end) = runtime.block_on(utils::measure_async(&provider, async {
138 // Simulate work with a small computation
139 let mut sum = 0;
140 for i in 0..1000 {
141 sum += i;
142 }
143 sum
144 }));
145
146 assert_eq!(result, 499500); // Sum of 0..1000
147 // End time should be greater than or equal to start time
148 assert!(end >= start);
149 }
150 }
151
152 #[cfg(not(feature = "std"))]
153 mod no_std_tests {
154 use super::{utils, TimestampProvider};
155
156 // Mock timestamp provider for no_std testing
157 struct MockNoStdTimestampProvider {
158 counter: core::sync::atomic::AtomicU64,
159 }
160
161 impl MockNoStdTimestampProvider {
162 fn new() -> Self {
163 Self {
164 counter: core::sync::atomic::AtomicU64::new(0),
165 }
166 }
167 }
168
169 impl TimestampProvider for MockNoStdTimestampProvider {
170 type Instant = u64;
171
172 fn now(&self) -> Self::Instant {
173 self.counter
174 .fetch_add(1, core::sync::atomic::Ordering::Relaxed)
175 }
176 }
177
178 #[test]
179 fn test_timeout_error_creation_no_std() {
180 let _error = utils::create_timeout_error("operation");
181 // In no_std mode, we just verify the error can be created
182 // without panicking
183 }
184
185 #[test]
186 fn test_measure_async_no_std() {
187 // Test that measure_async works in no_std with a simple timestamp provider
188 let provider = MockNoStdTimestampProvider::new();
189
190 // Since we can't use tokio in no_std, we'll test the function signature
191 // and basic functionality without actually running async code
192 let future = async {
193 // Simulate some work
194 42
195 };
196
197 // This mainly tests that the function compiles and accepts the right types
198 // We explicitly drop the future since we can't await it in no_std tests
199 core::mem::drop(utils::measure_async(&provider, future));
200 }
201 }
202}