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}