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}