browsr-types 0.4.0

Shared data models and schemas for Browsr browser automation flows.
Documentation
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::time::Duration;

use crate::{CommandPoint, FileType};

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct BrowserScreenshot {
    pub filename: String,
    pub data: String,
    pub format: String,
    pub size: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ElementBox {
    pub x: f64,
    pub y: f64,
    pub width: f64,
    pub height: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct BoundingBoxElement {
    pub selector: String,
    pub index: usize,
    pub tag: String,
    pub text: Option<String>,
    pub attributes: serde_json::Value,
    pub bounding_box: ElementBox,
    pub center: CommandPoint,
    pub visibility: bool,
    pub clickable: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub html: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct WaitForElementCheck {
    pub found: bool,
    pub visible: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ClickTarget {
    pub target_id: String,
    pub tag: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attributes: Option<serde_json::Map<String, Value>>,
    pub bounding_box: ElementBox,
    pub center: CommandPoint,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct DomElementDescriptor {
    pub index: usize,
    pub tag: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub selector: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub role: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attributes: Option<serde_json::Map<String, Value>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bounding_box: Option<ElementBox>,
    pub in_viewport: bool,
    pub clickable: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct DomSnapshot {
    pub url: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    pub viewport: (u32, u32),
    pub captured_at: i64,
    pub interactive: Vec<DomElementDescriptor>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub interactive_text: Option<String>,
}

#[derive(Debug, Clone)]
pub struct ObservationOptions {
    pub full_page: Option<bool>,
    pub with_overlay: bool,
    pub use_image: bool,
    pub include_content: bool,
    pub max_elements: usize,
    pub wait_for_load: bool,
    pub wait_timeout: Duration,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ObserveResponse {
    #[serde(flatten)]
    pub dom_snapshot: DomSnapshot,
    pub state_text: String,
    pub screenshot: Option<FileType>,
    pub click_targets: Vec<ClickTarget>,
}

impl ObservationOptions {
    pub fn for_command(full_page: Option<bool>, with_overlay: bool) -> Self {
        Self {
            full_page,
            with_overlay,
            use_image: true,
            include_content: false,
            max_elements: 140,
            wait_for_load: true,
            wait_timeout: Duration::from_millis(1500),
        }
    }

    pub fn lightweight(full_page: Option<bool>, with_overlay: bool, use_image: bool) -> Self {
        Self {
            use_image,
            include_content: false,
            ..Self::for_command(full_page, with_overlay)
        }
    }
}