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}