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