aimdb_core/context.rs
1//! Runtime context for AimDB services
2//!
3//! Provides a unified interface to runtime capabilities (time, sleep, logging)
4//! as a concrete value wrapping `Arc<dyn RuntimeOps>` — no runtime type
5//! parameter.
6
7use crate::executor::{BoxFuture, LogLevel, RuntimeOps};
8use alloc::sync::Arc;
9
10/// Unified runtime context for AimDB services
11///
12/// Wraps the dyn-safe [`RuntimeOps`] capability surface so services can use
13/// timing and logging without knowing the runtime adapter type. Cheap to
14/// clone (one `Arc`).
15#[derive(Clone)]
16pub struct RuntimeContext {
17 ops: Arc<dyn RuntimeOps>,
18}
19
20impl RuntimeContext {
21 /// Create a new RuntimeContext from the dyn-safe runtime capabilities.
22 pub fn new(ops: Arc<dyn RuntimeOps>) -> Self {
23 Self { ops }
24 }
25
26 /// Access time utilities
27 ///
28 /// Returns a time accessor for clock reads and sleeping.
29 pub fn time(&self) -> Time<'_> {
30 Time { ops: &*self.ops }
31 }
32
33 /// Access logging utilities
34 pub fn log(&self) -> Log<'_> {
35 Log { ops: &*self.ops }
36 }
37
38 /// Direct access to the underlying runtime capabilities.
39 pub fn ops(&self) -> &Arc<dyn RuntimeOps> {
40 &self.ops
41 }
42}
43
44/// Time utilities accessor for RuntimeContext
45///
46/// Durations are plain [`core::time::Duration`]; instants are `u64`
47/// nanoseconds from an arbitrary monotonic epoch (only differences between
48/// two readings are meaningful).
49pub struct Time<'a> {
50 ops: &'a dyn RuntimeOps,
51}
52
53impl Time<'_> {
54 /// Monotonic clock reading in nanoseconds from an arbitrary epoch.
55 ///
56 /// Never decreases; saturates at `u64::MAX` rather than wrapping.
57 pub fn now(&self) -> u64 {
58 self.ops.now_nanos()
59 }
60
61 /// Wall-clock time as `(seconds, nanoseconds)` since the Unix epoch, if
62 /// the runtime has a real-time clock (`None` on e.g. a bare MCU without
63 /// an RTC anchor).
64 pub fn unix_time(&self) -> Option<(u64, u32)> {
65 self.ops.unix_time()
66 }
67
68 /// Completes after at least `duration` has elapsed.
69 ///
70 /// The future is boxed (it crosses the `dyn RuntimeOps` boundary), so each
71 /// call costs one small heap allocation. That is fine for periodic loops
72 /// and timeouts; in a no_std hot path that sleeps every iteration, await
73 /// the adapter's native timer (e.g. `embassy_time::Timer`) directly
74 /// instead.
75 pub fn sleep(&self, duration: core::time::Duration) -> BoxFuture {
76 self.ops.sleep(duration)
77 }
78
79 /// Convenience: sleep for `ms` milliseconds.
80 pub fn sleep_millis(&self, ms: u64) -> BoxFuture {
81 self.ops.sleep(core::time::Duration::from_millis(ms))
82 }
83
84 /// Convenience: sleep for `secs` seconds.
85 pub fn sleep_secs(&self, secs: u64) -> BoxFuture {
86 self.ops.sleep(core::time::Duration::from_secs(secs))
87 }
88}
89
90/// Log utilities accessor for RuntimeContext
91///
92/// Provides structured logging operations.
93pub struct Log<'a> {
94 ops: &'a dyn RuntimeOps,
95}
96
97impl Log<'_> {
98 /// Log an informational message
99 pub fn info(&self, message: &str) {
100 self.ops.log(LogLevel::Info, message)
101 }
102
103 /// Log a debug message
104 pub fn debug(&self, message: &str) {
105 self.ops.log(LogLevel::Debug, message)
106 }
107
108 /// Log a warning message
109 pub fn warn(&self, message: &str) {
110 self.ops.log(LogLevel::Warn, message)
111 }
112
113 /// Log an error message
114 pub fn error(&self, message: &str) {
115 self.ops.log(LogLevel::Error, message)
116 }
117}