playwright_core/
channel.rs

1// Copyright 2024 Paul Adamson
2// Licensed under the Apache License, Version 2.0
3//
4// Channel - RPC communication proxy for ChannelOwner objects
5//
6// Architecture Reference:
7// - Python: playwright-python/playwright/_impl/_connection.py (Channel class)
8// - JavaScript: playwright/.../client/channelOwner.ts (_createChannel method)
9// - Java: Channels are implicit in method calls
10//
11// The Channel provides a typed interface for sending JSON-RPC messages
12// to the Playwright server on behalf of a ChannelOwner object.
13
14use crate::connection::ConnectionLike;
15use crate::error::Result;
16use serde::de::DeserializeOwned;
17use serde::Serialize;
18use serde_json::Value;
19use std::sync::Arc;
20
21/// Channel provides RPC communication for a ChannelOwner.
22///
23/// Every ChannelOwner has a Channel that sends method calls to the
24/// Playwright server and receives responses.
25///
26/// # Architecture
27///
28/// In the JavaScript implementation, Channel is a Proxy object that
29/// intercepts method calls and forwards them to the connection.
30///
31/// In Python, Channel is a class with explicit method forwarding.
32///
33/// In Rust, we provide an explicit `send` method that handles:
34/// - Serialization of parameters
35/// - Sending to connection with object's GUID
36/// - Waiting for response
37/// - Deserialization of result
38///
39/// # Example
40///
41/// ```ignore
42/// // Example of using Channel to send RPC calls
43/// use playwright_core::channel::Channel;
44/// use serde::{Serialize, Deserialize};
45///
46/// #[derive(Serialize)]
47/// struct LaunchParams {
48///     headless: bool,
49/// }
50///
51/// #[derive(Deserialize)]
52/// struct LaunchResult {
53///     browser: BrowserRef,
54/// }
55///
56/// // Protocol response references use Arc<str> for performance
57/// #[derive(Deserialize)]
58/// struct BrowserRef {
59///     guid: String, // Simplified for example; actual implementation uses Arc<str>
60/// }
61///
62/// async fn example(channel: &Channel) -> Result<(), Box<dyn std::error::Error>> {
63///     let params = LaunchParams { headless: true };
64///     let result: LaunchResult = channel.send("launch", params).await?;
65///     println!("Browser GUID: {}", result.browser.guid);
66///     Ok(())
67/// }
68/// ```
69#[derive(Clone)]
70pub struct Channel {
71    guid: Arc<str>,
72    connection: Arc<dyn ConnectionLike>,
73}
74
75impl Channel {
76    /// Creates a new Channel for the given object GUID.
77    ///
78    /// # Arguments
79    /// * `guid` - The GUID of the ChannelOwner this channel represents
80    /// * `connection` - The connection to send messages through
81    pub fn new(guid: Arc<str>, connection: Arc<dyn ConnectionLike>) -> Self {
82        Self { guid, connection }
83    }
84
85    /// Sends a method call to the Playwright server and awaits the response.
86    ///
87    /// This method:
88    /// 1. Serializes `params` to JSON
89    /// 2. Sends a JSON-RPC request to the server via the connection
90    /// 3. Waits for the response (correlated by request ID)
91    /// 4. Deserializes the response to type `R`
92    /// 5. Returns the result or an error
93    ///
94    /// # Type Parameters
95    /// * `P` - Parameter type (must be serializable)
96    /// * `R` - Result type (must be deserializable)
97    ///
98    /// # Arguments
99    /// * `method` - The method name to call (e.g., "launch", "goto")
100    /// * `params` - The parameters to send
101    ///
102    /// # Example
103    ///
104    /// ```ignore
105    /// # use playwright_core::channel::Channel;
106    /// # use serde::{Serialize, Deserialize};
107    /// # async fn example(channel: &Channel) -> Result<(), Box<dyn std::error::Error>> {
108    /// #[derive(Serialize)]
109    /// struct GotoParams<'a> {
110    ///     url: &'a str,
111    /// }
112    ///
113    /// #[derive(Deserialize)]
114    /// struct GotoResult {}
115    ///
116    /// let params = GotoParams { url: "https://example.com" };
117    /// let _result: GotoResult = channel.send("goto", params).await?;
118    /// # Ok(())
119    /// # }
120    /// ```
121    pub async fn send<P: Serialize, R: DeserializeOwned>(
122        &self,
123        method: &str,
124        params: P,
125    ) -> Result<R> {
126        // Serialize params to JSON
127        let params_value = serde_json::to_value(params)?;
128
129        // Send message via connection
130        let response = self
131            .connection
132            .send_message(&self.guid, method, params_value)
133            .await?;
134
135        // Deserialize response
136        serde_json::from_value(response).map_err(Into::into)
137    }
138
139    /// Sends a method call with no parameters.
140    ///
141    /// Convenience method for calls that don't need parameters.
142    pub async fn send_no_params<R: DeserializeOwned>(&self, method: &str) -> Result<R> {
143        self.send(method, Value::Null).await
144    }
145
146    /// Sends a method call that returns no result (void).
147    ///
148    /// Convenience method for fire-and-forget calls.
149    pub async fn send_no_result<P: Serialize>(&self, method: &str, params: P) -> Result<()> {
150        let _: Value = self.send(method, params).await?;
151        Ok(())
152    }
153
154    /// Returns the GUID this channel represents.
155    pub fn guid(&self) -> &str {
156        &self.guid
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    #[test]
163    fn test_channel_creation() {
164        // Channel creation will be tested in integration tests
165        // with a real Connection
166    }
167
168    #[test]
169    fn test_channel_send() {
170        // Channel send will be tested in integration tests
171        // with a real server connection
172    }
173}