Skip to main content

browsr_types/
observe.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::time::Duration;
5
6use crate::{CommandPoint, FileType};
7
8#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
9pub struct BrowserScreenshot {
10    pub filename: String,
11    pub data: String,
12    pub format: String,
13    pub size: usize,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
17pub struct ElementBox {
18    pub x: f64,
19    pub y: f64,
20    pub width: f64,
21    pub height: f64,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
25pub struct BoundingBoxElement {
26    pub selector: String,
27    pub index: usize,
28    pub tag: String,
29    pub text: Option<String>,
30    pub attributes: serde_json::Value,
31    pub bounding_box: ElementBox,
32    pub center: CommandPoint,
33    pub visibility: bool,
34    pub clickable: bool,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub html: Option<String>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
40pub struct WaitForElementCheck {
41    pub found: bool,
42    pub visible: bool,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
46pub struct ClickTarget {
47    pub target_id: String,
48    pub tag: String,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub text: Option<String>,
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub attributes: Option<serde_json::Map<String, Value>>,
53    pub bounding_box: ElementBox,
54    pub center: CommandPoint,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
58pub struct DomElementDescriptor {
59    pub index: usize,
60    pub tag: String,
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub selector: Option<String>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub role: Option<String>,
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub name: Option<String>,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub text: Option<String>,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub attributes: Option<serde_json::Map<String, Value>>,
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub bounding_box: Option<ElementBox>,
73    pub in_viewport: bool,
74    pub clickable: bool,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
78pub struct DomSnapshot {
79    pub url: String,
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub title: Option<String>,
82    pub viewport: (u32, u32),
83    pub captured_at: i64,
84    pub interactive: Vec<DomElementDescriptor>,
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub interactive_text: Option<String>,
87}
88
89#[derive(Debug, Clone)]
90pub struct ObservationOptions {
91    pub full_page: Option<bool>,
92    pub with_overlay: bool,
93    pub use_image: bool,
94    pub include_content: bool,
95    pub max_elements: usize,
96    pub wait_for_load: bool,
97    pub wait_timeout: Duration,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
101pub struct ObserveResponse {
102    #[serde(flatten)]
103    pub dom_snapshot: DomSnapshot,
104    pub state_text: String,
105    pub screenshot: Option<FileType>,
106    pub click_targets: Vec<ClickTarget>,
107}
108
109impl ObservationOptions {
110    pub fn for_command(full_page: Option<bool>, with_overlay: bool) -> Self {
111        Self {
112            full_page,
113            with_overlay,
114            use_image: true,
115            include_content: false,
116            max_elements: 140,
117            wait_for_load: true,
118            wait_timeout: Duration::from_millis(1500),
119        }
120    }
121
122    pub fn lightweight(full_page: Option<bool>, with_overlay: bool, use_image: bool) -> Self {
123        Self {
124            use_image,
125            include_content: false,
126            ..Self::for_command(full_page, with_overlay)
127        }
128    }
129}