cu29_runtime/context.rs
1//! User-facing execution context passed to task and bridge process callbacks.
2
3use core::ops::Deref;
4use cu29_clock::{RobotClock, RobotClockMock};
5
6/// Execution context passed to task and bridge callbacks.
7///
8/// `CuContext` provides callback code with:
9/// - time access through `clock` and `Deref<Target = RobotClock>`
10/// - current execution sequence id via `cl_id()`
11/// - current task metadata via `task_id()` / `task_index()`
12///
13/// The execution sequence id matches the copper-list id of the iteration being
14/// processed. It is also available in other lifecycle callbacks
15/// (`start`/`preprocess`/`postprocess`/`stop`) for continuity, but outside
16/// `process` callbacks it must not be treated as a live copper-list handle.
17///
18/// The runtime creates one context per execution loop and updates transient
19/// fields such as the currently executing task before each callback.
20#[derive(Clone, Debug)]
21pub struct CuContext {
22 /// Runtime clock. Kept as a field for direct access (`context.clock.now()`).
23 pub clock: RobotClock,
24 cl_id: u64,
25 task_ids: &'static [&'static str],
26 current_task_index: Option<usize>,
27}
28
29impl CuContext {
30 /// Starts a context builder from a clock.
31 pub fn builder(clock: RobotClock) -> CuContextBuilder {
32 CuContextBuilder {
33 clock,
34 cl_id: 0,
35 task_ids: &[],
36 }
37 }
38
39 /// Creates a context from an existing clock with default metadata.
40 ///
41 /// Defaults:
42 /// - `cl_id = 0`
43 /// - no task id table
44 pub fn from_clock(clock: RobotClock) -> Self {
45 Self::builder(clock).build()
46 }
47
48 /// Creates a context backed by a real robot clock.
49 ///
50 /// Defaults:
51 /// - `cl_id = 0`
52 /// - no task id table
53 #[cfg(feature = "std")]
54 pub fn new_with_clock() -> Self {
55 Self::from_clock(RobotClock::new())
56 }
57
58 /// Creates a context backed by a mock clock.
59 ///
60 /// Returns both the context and its [`RobotClockMock`] control handle.
61 pub fn new_mock_clock() -> (Self, RobotClockMock) {
62 let (clock, mock) = RobotClock::mock();
63 (Self::from_clock(clock), mock)
64 }
65
66 /// Internal constructor used by runtime internals and code generation.
67 pub(crate) fn new(clock: RobotClock, clid: u64, task_ids: &'static [&'static str]) -> Self {
68 Self {
69 clock,
70 cl_id: clid,
71 task_ids,
72 current_task_index: None,
73 }
74 }
75
76 /// Sets the currently executing task index.
77 pub fn set_current_task(&mut self, task_index: usize) {
78 self.current_task_index = Some(task_index);
79 }
80
81 /// Clears the currently executing task.
82 pub fn clear_current_task(&mut self) {
83 self.current_task_index = None;
84 }
85
86 /// Returns the current execution sequence id.
87 ///
88 /// In `process` callbacks, this value is the id of the copper-list being
89 /// processed. In other lifecycle callbacks, this value is still meaningful
90 /// for sequencing but does not imply that a copper-list instance is alive.
91 pub fn cl_id(&self) -> u64 {
92 self.cl_id
93 }
94
95 /// Returns the current task index, if any.
96 pub fn task_index(&self) -> Option<usize> {
97 self.current_task_index
98 }
99
100 /// Returns the current task id, if any.
101 pub fn task_id(&self) -> Option<&'static str> {
102 self.current_task_index
103 .and_then(|idx| self.task_ids.get(idx).copied())
104 }
105}
106
107/// Builder for [`CuContext`].
108#[derive(Clone, Debug)]
109pub struct CuContextBuilder {
110 clock: RobotClock,
111 cl_id: u64,
112 task_ids: &'static [&'static str],
113}
114
115impl CuContextBuilder {
116 /// Sets the copper-list id for the context.
117 pub fn cl_id(mut self, cl_id: u64) -> Self {
118 self.cl_id = cl_id;
119 self
120 }
121
122 /// Sets the static task id table for task metadata access.
123 pub fn task_ids(mut self, task_ids: &'static [&'static str]) -> Self {
124 self.task_ids = task_ids;
125 self
126 }
127
128 /// Builds a context value.
129 pub fn build(self) -> CuContext {
130 CuContext::new(self.clock, self.cl_id, self.task_ids)
131 }
132}
133
134impl Deref for CuContext {
135 type Target = RobotClock;
136
137 fn deref(&self) -> &Self::Target {
138 &self.clock
139 }
140}