actr_framework/context.rs
1//! Context trait - Execution context interface for actors
2
3use actr_protocol::{ActorResult, ActrId, ActrType, DataStream};
4use async_trait::async_trait;
5use futures_util::future::BoxFuture;
6
7/// Context - Actor execution context interface
8///
9/// Defines the complete interface for Actor interaction with the system, including:
10/// - Context data access (self_id, trace_id, etc.)
11/// - Communication capabilities (call, tell)
12///
13/// # Design Principles
14///
15/// - **Interface only**: Framework does not provide implementation, runtime implements
16/// - **Generic parameter**: User code uses `<C: Context>` instead of `&dyn Context`
17/// - **Zero virtual calls**: Static dispatch via generic monomorphization
18///
19/// # Implementation Requirements
20///
21/// Runtime must implement this trait and use enum dispatch or other
22/// zero-overhead mechanisms internally (avoiding virtual function calls).
23///
24/// # Example
25///
26/// ```rust,ignore
27/// async fn my_handler<C: Context>(ctx: &C) {
28/// // Access data
29/// let id = ctx.self_id();
30/// let trace = ctx.trace_id();
31///
32/// // Type-safe call
33/// let response = ctx.call(&target, request).await?;
34/// }
35/// ```
36#[async_trait]
37pub trait Context: Send + Sync {
38 // ========== Data Access Methods ==========
39
40 /// Get the current Actor's ID
41 fn self_id(&self) -> &ActrId;
42
43 /// Get the caller's Actor ID
44 ///
45 /// - `Some(caller_id)`: Called by another Actor
46 /// - `None`: System internal call (e.g., lifecycle hooks)
47 fn caller_id(&self) -> Option<&ActrId>;
48
49 /// Get the distributed trace ID
50 ///
51 /// Remains constant throughout the call chain, used for log correlation and request path tracing.
52 fn trace_id(&self) -> &str;
53
54 /// Get the unique request ID
55 ///
56 /// A new request_id is generated for each RPC call, used to match requests and responses.
57 fn request_id(&self) -> &str;
58
59 // ========== Communication Methods ==========
60
61 /// Send a type-safe RPC request and wait for response
62 ///
63 /// This is the primary way to call other Actors, providing full type safety guarantees.
64 ///
65 /// # Type Inference
66 ///
67 /// Response type is automatically inferred from `R::Response`, no manual annotation needed:
68 ///
69 /// ```rust,ignore
70 /// let request = EchoRequest { message: "hello".to_string() };
71 /// let response: EchoResponse = ctx.call(&target, request).await?;
72 /// // ^^^^^^^^^^^^ Inferred from EchoRequest::Response
73 /// ```
74 ///
75 /// # Error Handling
76 ///
77 /// - `ProtocolError::TransportError`: Network transport failure
78 /// - `ProtocolError::Actr(DecodeFailure)`: Response decode failure
79 /// - `ProtocolError::Actr(UnknownRoute)`: Route does not exist
80 /// - Errors returned by remote Actor's business logic
81 ///
82 /// # Parameters
83 ///
84 /// - `target`: Target destination (`Dest::Shell` for local, `Dest::Actor(id)` for remote)
85 /// - `request`: Request message implementing `RpcRequest` trait
86 ///
87 /// # Returns
88 ///
89 /// Returns response message of type `R::Response`
90 async fn call<R: actr_protocol::RpcRequest>(
91 &self,
92 target: &crate::Dest,
93 request: R,
94 ) -> ActorResult<R::Response>;
95
96 /// Send a type-safe one-way message (no response expected)
97 ///
98 /// Used for sending notifications, events, etc. that do not require a response.
99 ///
100 /// # Semantics
101 ///
102 /// - **Fire-and-forget**: Does not wait for response after sending
103 /// - **No delivery guarantee**: Message may be lost if target is unreachable
104 /// - **Low latency**: Does not block waiting for response
105 ///
106 /// # Parameters
107 ///
108 /// - `target`: Target destination (`Dest::Shell` for local, `Dest::Actor(id)` for remote)
109 /// - `message`: Message implementing `RpcRequest` trait
110 async fn tell<R: actr_protocol::RpcRequest>(
111 &self,
112 target: &crate::Dest,
113 message: R,
114 ) -> ActorResult<()>;
115
116 // ========== Fast Path: DataStream Methods ==========
117
118 /// Register a DataStream callback for a specific stream
119 ///
120 /// When a DataStream with matching stream_id arrives, the registered callback will be invoked.
121 /// Callbacks are executed concurrently and do not block other streams.
122 ///
123 /// # Parameters
124 ///
125 /// - `stream_id`: Stream identifier (must be globally unique)
126 /// - `callback`: Handler function that receives (DataStream, sender ActrId)
127 ///
128 /// # Example
129 ///
130 /// ```rust,ignore
131 /// ctx.register_stream("log-stream", |chunk, sender| {
132 /// Box::pin(async move {
133 /// println!("Received chunk {} from {:?}", chunk.sequence, sender);
134 /// Ok(())
135 /// })
136 /// }).await?;
137 /// ```
138 async fn register_stream<F>(&self, stream_id: String, callback: F) -> ActorResult<()>
139 where
140 F: Fn(DataStream, ActrId) -> BoxFuture<'static, ActorResult<()>> + Send + Sync + 'static;
141
142 /// Unregister a DataStream callback
143 ///
144 /// # Parameters
145 ///
146 /// - `stream_id`: Stream identifier to unregister
147 async fn unregister_stream(&self, stream_id: &str) -> ActorResult<()>;
148
149 /// Send a DataStream to a destination
150 ///
151 /// DataStreams are sent via Fast Path with configurable QoS based on PayloadType.
152 ///
153 /// # Parameters
154 ///
155 /// - `target`: Target destination
156 /// - `chunk`: DataStream to send
157 async fn send_data_stream(&self, target: &crate::Dest, chunk: DataStream) -> ActorResult<()>;
158
159 /// Discover a remote Actor of the specified type via the signaling server.
160 ///
161 /// Returns a route candidate or an error if none are available. Concrete
162 /// selection strategy is decided by the Context implementation.
163 async fn discover_route_candidate(&self, target_type: &ActrType) -> ActorResult<ActrId>;
164
165 // ========== Fast Path: MediaTrack Methods (WebRTC Native) ==========
166
167 /// Register a WebRTC native media track callback
168 ///
169 /// When media samples arrive on the specified track, the registered callback will be invoked.
170 /// Uses WebRTC native RTCTrackRemote, no protobuf serialization overhead.
171 ///
172 /// # Parameters
173 ///
174 /// - `track_id`: Media track identifier (must match WebRTC track ID in SDP)
175 /// - `callback`: Handler function that receives native media samples
176 ///
177 /// # Example
178 ///
179 /// ```rust,ignore
180 /// use actr_framework::MediaSample;
181 ///
182 /// ctx.register_media_track("video-track-1", |sample, sender| {
183 /// Box::pin(async move {
184 /// // Decode and render video frame (native RTP payload)
185 /// println!("Received {} bytes at timestamp {}",
186 /// sample.data.len(), sample.timestamp);
187 /// decoder.decode(&sample.data).await?;
188 /// Ok(())
189 /// })
190 /// }).await?;
191 /// ```
192 ///
193 /// # Architecture Note
194 ///
195 /// MediaTrack uses WebRTC native RTP channels (RTCTrackRemote), NOT DataChannel.
196 /// This provides:
197 /// - Zero protobuf serialization overhead
198 /// - Native RTP header information (timestamp, SSRC, etc.)
199 /// - Optimal latency (~1-2ms lower than DataChannel)
200 async fn register_media_track<F>(&self, track_id: String, callback: F) -> ActorResult<()>
201 where
202 F: Fn(MediaSample, ActrId) -> BoxFuture<'static, ActorResult<()>> + Send + Sync + 'static;
203
204 /// Unregister a media track callback
205 ///
206 /// # Parameters
207 ///
208 /// - `track_id`: Media track identifier to unregister
209 async fn unregister_media_track(&self, track_id: &str) -> ActorResult<()>;
210
211 /// Send media samples via WebRTC native track
212 ///
213 /// Sends raw media samples through WebRTC RTCRtpSender (native RTP).
214 /// This is much more efficient than sending through DataChannel.
215 ///
216 /// # Parameters
217 ///
218 /// - `target`: Target destination
219 /// - `track_id`: Track identifier
220 /// - `sample`: Media sample to send
221 ///
222 /// # Example
223 ///
224 /// ```rust,ignore
225 /// use actr_framework::{MediaSample, MediaType};
226 ///
227 /// let sample = MediaSample {
228 /// data: encoded_frame.into(),
229 /// timestamp: rtp_timestamp,
230 /// codec: "H264".to_string(),
231 /// media_type: MediaType::Video,
232 /// };
233 ///
234 /// ctx.send_media_sample(&target, "video-track-1", sample).await?;
235 /// ```
236 async fn send_media_sample(
237 &self,
238 target: &crate::Dest,
239 track_id: &str,
240 sample: MediaSample,
241 ) -> ActorResult<()>;
242}
243
244/// Media sample data from WebRTC native track
245///
246/// Lightweight wrapper around WebRTC native RTP sample.
247#[derive(Clone)]
248pub struct MediaSample {
249 /// Raw sample data (encoded audio/video frame)
250 pub data: bytes::Bytes,
251
252 /// Sample timestamp (from RTP timestamp)
253 pub timestamp: u32,
254
255 /// Codec-specific information
256 pub codec: String,
257
258 /// Media type (audio or video)
259 pub media_type: MediaType,
260}
261
262/// Media type enum
263#[derive(Clone, Copy, Debug, PartialEq, Eq)]
264pub enum MediaType {
265 Audio,
266 Video,
267}