pebbles 0.0.102

A unfinished webautomation framework for working with Firefox and the geckodriver.
Documentation
use crate::service::Service;
use crate::{raise, error};
use crate::rect::Rect;
use crate::result::{PebblesError,PebblesErrorDetails};
use std::rc::Rc;
use serde_json::{Value, json};

use serde::{ser::SerializeStruct, Serializer};

/// Represents a web element.
///
/// The `WebElement` struct is a representation of a web element. It contains a reference to the `Service` 
/// which allows it to perform operations like fetching the element's attributes.
pub struct WebElement {
    service: Rc<Service>,
    id:String,
}

impl WebElement {
    /// Construct a new `WebElement`.
    ///
    /// This method takes a reference-counted `Service` and an elements id string, and returns a new `WebElement`.
    pub fn new(service: Rc<Service>, id:&str) -> Self {
        WebElement {
            service,
            id:id.to_string(),
        }
    }
    // region: Properties
    pub fn selected(&self) -> Result<bool, PebblesError> {
        let json = self.service.get(&format!("/element/{}/selected", self.id))?
        .json::<Value>()?;

        let value = match json.get("value") {
            Some(value) => value,
            None => raise!(PebblesError::PhrasingError, "Invalid value for \"selected\" from webdriver."),
        };

        match value.as_bool() {
            Some(value) => Ok(value),
            None => raise!(PebblesError::PhrasingError, "Invalid value for \"selected\" from webdriver."),
        }
    }
    // endregion: Properties

    /// Fetch the rectangle of the `WebElement`.
    ///
    /// This method sends a request to the `Service` to fetch the rectangle of the `WebElement`. It returns 
    /// a `Result` that contains a `Rect` on success, or a `PebblesError` on failure.    
    pub fn rect(&self) -> Result<Rect, PebblesError> {
        fn get_value_as_i64(json_value: &Value, key: &str) -> Result<i64, PebblesError> {
            Ok(json_value.get(key)
                .ok_or_else(|| error!(PebblesError::PhrasingError, "Invalid Rect values from webdriver.", json_value.clone()))?
                .as_f64()
                .ok_or_else(|| error!(PebblesError::PhrasingError, "Invalid Rect values from webdriver.", json_value.clone()))? as i64
            )
        }
        let json_rect: Value = self.service
            .get(&format!("/element/{}/rect", self.id))?
            .json::<Value>()?
            .get("value")
            .ok_or_else(|| error!(PebblesError::PhrasingError, "Invalid Rect values from webdriver."))?
            .clone();
        Ok(Rect {
            x: get_value_as_i64(&json_rect, "x")?,
            y: get_value_as_i64(&json_rect, "y")?,
            width: get_value_as_i64(&json_rect, "width")?,
            height: get_value_as_i64(&json_rect, "height")?,
        })
    }

    /// Fetch an attribute of the `WebElement`.
    ///
    /// This method takes the name of an attribute and sends a request to the `Service` to fetch the value 
    /// of that attribute. It returns a `Result` that contains the attribute value on success, or a 
    /// `PebblesError` on failure.
    pub fn attribute(&self, name:&str) -> Result<Value, PebblesError> {
        let java_script: &'static str = include_str!("attribute.js");
        Ok(self
            .service
            .post(
                "/execute/sync",
                &json!({"script": format!("/* getAttribute */return ({}).apply(null, arguments);", java_script).as_str(), "args": [self, name]}),
            )?
            .json::<Value>()?["value"]
            .clone())
    }
}

use std::cmp::{ Ord, PartialOrd };

impl Ord for WebElement {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        match self.rect() {
            Ok(self_rect) => match other.rect() {
                Ok(other_rect) => self_rect.y.cmp(&other_rect.y),
                Err(_) => std::cmp::Ordering::Less,
            },
            Err(_) => std::cmp::Ordering::Greater,
        }
    }
}

impl PartialEq for WebElement {
    fn eq(&self, other: &Self) -> bool {
        match self.rect() {
            Ok(self_rect) => match other.rect() {
                Ok(other_rect) => self_rect.y == other_rect.y,
                Err(_) => false,
            },
            Err(_) => false,
        }
    }
}

impl Eq for WebElement {}

impl PartialOrd for WebElement {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl std::fmt::Display for WebElement {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.id)
    }
}

impl serde::Serialize for WebElement {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state = serializer.serialize_struct("WebElement", 1)?;
        state.serialize_field("element-6066-11e4-a52e-4f735466cecf", &self.id)?;
        state.end()
    }
}