playwright_rs/protocol/
worker.rs1use crate::error::Result;
4use crate::protocol::evaluate_conversion::{parse_result, serialize_argument, serialize_null};
5use crate::server::channel::Channel;
6use crate::server::channel_owner::{ChannelOwner, ChannelOwnerImpl, ParentOrConnection};
7use crate::server::connection::ConnectionExt;
8use serde::de::DeserializeOwned;
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11use std::any::Any;
12use std::sync::Arc;
13
14#[derive(Clone)]
42pub struct Worker {
43 base: ChannelOwnerImpl,
44 url: String,
46}
47
48impl Worker {
49 pub fn new(
54 parent: Arc<dyn ChannelOwner>,
55 type_name: String,
56 guid: Arc<str>,
57 initializer: Value,
58 ) -> Result<Self> {
59 let url = initializer
60 .get("url")
61 .and_then(|v| v.as_str())
62 .unwrap_or("")
63 .to_string();
64
65 let base = ChannelOwnerImpl::new(
66 ParentOrConnection::Parent(parent),
67 type_name,
68 guid,
69 initializer,
70 );
71
72 Ok(Self { base, url })
73 }
74
75 pub fn url(&self) -> &str {
79 &self.url
80 }
81
82 fn channel(&self) -> &Channel {
84 self.base.channel()
85 }
86
87 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
103 pub async fn evaluate<R, T>(&self, expression: &str, arg: Option<T>) -> Result<R>
104 where
105 R: DeserializeOwned,
106 T: Serialize,
107 {
108 let serialized_arg = match arg {
109 Some(a) => serialize_argument(&a),
110 None => serialize_null(),
111 };
112
113 let params = serde_json::json!({
114 "expression": expression,
115 "arg": serialized_arg
116 });
117
118 #[derive(Deserialize)]
119 struct EvaluateResult {
120 value: Value,
121 }
122
123 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
124 let parsed = parse_result(&result.value);
125
126 serde_json::from_value(parsed).map_err(|e| {
127 crate::error::Error::ProtocolError(format!("Failed to deserialize result: {}", e))
128 })
129 }
130
131 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
142 pub async fn evaluate_handle(
143 &self,
144 expression: &str,
145 ) -> Result<Arc<crate::protocol::JSHandle>> {
146 let trimmed = expression.trim();
147 let is_function = trimmed.starts_with('(')
148 || trimmed.starts_with("function")
149 || trimmed.starts_with("async ");
150
151 let params = serde_json::json!({
152 "expression": expression,
153 "isFunction": is_function,
154 "arg": {"value": {"v": "undefined"}, "handles": []}
155 });
156
157 #[derive(Deserialize)]
158 struct HandleRef {
159 guid: String,
160 }
161 #[derive(Deserialize)]
162 struct EvaluateHandleResponse {
163 handle: HandleRef,
164 }
165
166 let response: EvaluateHandleResponse = self
167 .channel()
168 .send("evaluateExpressionHandle", params)
169 .await?;
170
171 let guid = &response.handle.guid;
172 let connection = self.base.connection();
173 let mut attempts = 0;
174 let max_attempts = 20;
175
176 let handle = loop {
177 match connection
178 .get_typed::<crate::protocol::JSHandle>(guid)
179 .await
180 {
181 Ok(h) => break h,
182 Err(_) if attempts < max_attempts => {
183 attempts += 1;
184 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
185 }
186 Err(e) => return Err(e),
187 }
188 };
189
190 Ok(Arc::new(handle))
191 }
192}
193
194impl ChannelOwner for Worker {
195 fn guid(&self) -> &str {
196 self.base.guid()
197 }
198
199 fn type_name(&self) -> &str {
200 self.base.type_name()
201 }
202
203 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
204 self.base.parent()
205 }
206
207 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
208 self.base.connection()
209 }
210
211 fn initializer(&self) -> &Value {
212 self.base.initializer()
213 }
214
215 fn channel(&self) -> &Channel {
216 self.base.channel()
217 }
218
219 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
220 self.base.dispose(reason)
221 }
222
223 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
224 self.base.adopt(child)
225 }
226
227 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
228 self.base.add_child(guid, child)
229 }
230
231 fn remove_child(&self, guid: &str) {
232 self.base.remove_child(guid)
233 }
234
235 fn on_event(&self, _method: &str, _params: Value) {
236 }
238
239 fn was_collected(&self) -> bool {
240 self.base.was_collected()
241 }
242
243 fn as_any(&self) -> &dyn Any {
244 self
245 }
246}
247
248impl std::fmt::Debug for Worker {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 f.debug_struct("Worker")
251 .field("guid", &self.guid())
252 .field("url", &self.url)
253 .finish()
254 }
255}