use crate::error::Result;
use crate::protocol::evaluate_conversion::parse_result;
use crate::server::channel_owner::{ChannelOwner, ChannelOwnerImpl, ParentOrConnection};
use crate::server::connection::ConnectionExt;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Clone)]
pub struct JSHandle {
base: ChannelOwnerImpl,
}
impl JSHandle {
pub fn new(
parent: Arc<dyn ChannelOwner>,
type_name: String,
guid: Arc<str>,
initializer: Value,
) -> Result<Self> {
let base = ChannelOwnerImpl::new(
ParentOrConnection::Parent(parent),
type_name,
guid,
initializer,
);
Ok(Self { base })
}
pub async fn json_value(&self) -> Result<Value> {
#[derive(Deserialize)]
struct JsonValueResponse {
value: Value,
}
let response: JsonValueResponse = self
.base
.channel()
.send("jsonValue", serde_json::json!({}))
.await?;
Ok(parse_result(&response.value))
}
pub async fn get_property(&self, name: &str) -> Result<JSHandle> {
#[derive(Deserialize)]
struct HandleRef {
guid: String,
}
#[derive(Deserialize)]
struct GetPropertyResponse {
handle: HandleRef,
}
let response: GetPropertyResponse = self
.base
.channel()
.send(
"getProperty",
serde_json::json!({
"name": name
}),
)
.await?;
let guid = &response.handle.guid;
let connection = self.base.connection();
let mut attempts = 0;
let max_attempts = 20;
let handle = loop {
match connection.get_typed::<JSHandle>(guid).await {
Ok(h) => break h,
Err(_) if attempts < max_attempts => {
attempts += 1;
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
Err(e) => return Err(e),
}
};
Ok(handle)
}
pub async fn get_properties(&self) -> Result<HashMap<String, JSHandle>> {
#[derive(Deserialize)]
struct PropertyEntry {
name: String,
value: HandleRef,
}
#[derive(Deserialize)]
struct HandleRef {
guid: String,
}
#[derive(Deserialize)]
struct GetPropertiesResponse {
properties: Vec<PropertyEntry>,
}
let response: GetPropertiesResponse = self
.base
.channel()
.send("getPropertyList", serde_json::json!({}))
.await?;
let connection = self.base.connection();
let mut map = HashMap::new();
for entry in response.properties {
let guid = &entry.name.clone();
let handle_guid = &entry.value.guid;
let mut attempts = 0;
let max_attempts = 20;
let handle = loop {
match connection.get_typed::<JSHandle>(handle_guid).await {
Ok(h) => break h,
Err(_) if attempts < max_attempts => {
attempts += 1;
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
Err(e) => return Err(e),
}
};
map.insert(guid.clone(), handle);
}
Ok(map)
}
pub async fn evaluate<R, T>(&self, expression: &str, arg: Option<&T>) -> Result<R>
where
R: DeserializeOwned,
T: Serialize,
{
let _ = arg;
let params = serde_json::json!({
"expression": expression,
"isFunction": true,
"arg": {
"value": {"h": 0},
"handles": [{"guid": self.base.guid()}]
}
});
#[derive(Deserialize)]
struct EvaluateResult {
value: Value,
}
let result: EvaluateResult = self
.base
.channel()
.send("evaluateExpression", params)
.await?;
let parsed = parse_result(&result.value);
serde_json::from_value(parsed).map_err(|e| {
crate::error::Error::ProtocolError(format!("Failed to deserialize result: {}", e))
})
}
pub async fn evaluate_handle<T>(&self, expression: &str, arg: Option<&T>) -> Result<JSHandle>
where
T: Serialize,
{
let _ = arg;
let params = serde_json::json!({
"expression": expression,
"isFunction": true,
"arg": {
"value": {"h": 0},
"handles": [{"guid": self.base.guid()}]
}
});
#[derive(Deserialize)]
struct HandleRef {
guid: String,
}
#[derive(Deserialize)]
struct EvaluateHandleResponse {
handle: HandleRef,
}
let response: EvaluateHandleResponse = self
.base
.channel()
.send("evaluateExpressionHandle", params)
.await?;
let guid = &response.handle.guid;
let connection = self.base.connection();
let mut attempts = 0;
let max_attempts = 20;
let handle = loop {
match connection.get_typed::<JSHandle>(guid).await {
Ok(h) => break h,
Err(_) if attempts < max_attempts => {
attempts += 1;
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
Err(e) => return Err(e),
}
};
Ok(handle)
}
pub async fn dispose(&self) -> Result<()> {
self.base
.channel()
.send_no_result("dispose", serde_json::json!({}))
.await
}
pub fn as_element_type_name(&self) -> Option<&str> {
if self.base.type_name() == "ElementHandle" {
Some(self.base.guid())
} else {
None
}
}
}
impl ChannelOwner for JSHandle {
fn guid(&self) -> &str {
self.base.guid()
}
fn type_name(&self) -> &str {
self.base.type_name()
}
fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
self.base.parent()
}
fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
self.base.connection()
}
fn initializer(&self) -> &Value {
self.base.initializer()
}
fn channel(&self) -> &crate::server::channel::Channel {
self.base.channel()
}
fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
self.base.dispose(reason)
}
fn adopt(&self, child: Arc<dyn ChannelOwner>) {
self.base.adopt(child)
}
fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
self.base.add_child(guid, child)
}
fn remove_child(&self, guid: &str) {
self.base.remove_child(guid)
}
fn on_event(&self, _method: &str, _params: Value) {
}
fn was_collected(&self) -> bool {
self.base.was_collected()
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl std::fmt::Debug for JSHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("JSHandle")
.field("guid", &self.guid())
.finish()
}
}