pebbles 0.0.1

A unfinished webautomation framework for working with Firefox and the geckodriver.
Documentation
use crate::{
    keyboard::Keyboard,
    mouse::Mouse,
    result::{PebblesError, PebblesErrorDetails},
    service::Service,
    webelement::WebElement,
};

use serde_json::{json, Value};
use std::rc::Rc;

pub struct Pebbles {
    pub service: Rc<Service>,
    pub mouse: Mouse,
    pub keyboard: Keyboard,
}

impl Pebbles {
    pub fn new(geckodriver_cmd: &str, capabilities: Option<&Value>) -> Result<Self, PebblesError> {
        let pebbles_capabilities = match capabilities {
            Some(caps) => caps.clone(),
            None => json!({
                "capabilities": {
                    "firstMatch": [{}],
                    "alwaysMatch": {
                        "browserName": "firefox",
                        "acceptInsecureCerts": true,
                        "moz:debuggerAddress": true,
                        "pageLoadStrategy": "normal",
                        "moz:firefoxOptions": {
                            "prefs": {},
                            "args": [],
                        }
                    }
                }
            }),
        };
        let service = match Service::new(&pebbles_capabilities, geckodriver_cmd) {
            Ok(response) => Rc::new(response),
            Err(error) => return Err(error),
        };
        let mouse: Mouse = Mouse::new(service.clone());
        let keyboard: Keyboard = Keyboard::new(service.clone());
        Ok(Pebbles {
            service,
            mouse,
            keyboard,
        })
    }

    // region: Navigation
    pub fn get(&self, url: &str) -> Result<(), PebblesError> {
        self.service.post("/url", &json!({"url":url}))?;
        Ok(())
    }

    pub fn url(&self) -> Result<String, PebblesError> {
        let response = self.service.get("/url")?;
        let response_json = response.json::<Value>()?;
        match response_json["value"].as_str() {
            Some(url) => Ok(url.to_string()),
            None => Err(PebblesError::PhrasingError(PebblesErrorDetails::new(
                "Json value is not a string",
            ))),
        }
    }

    pub fn back(&self) -> Result<(), PebblesError> {
        self.service.post("/back", &json!({}))?;
        Ok(())
    }

    pub fn forward(&self) -> Result<(), PebblesError> {
        self.service.post("/forward", &json!({}))?;
        Ok(())
    }

    pub fn refresh(&self) -> Result<(), PebblesError> {
        self.service.post("/refresh", &json!({}))?;
        Ok(())
    }

    pub fn title(&self) -> Result<String, PebblesError> {
        let response = self.service.get("/title")?;
        let response_json = response.json::<Value>()?;
        match response_json["value"].as_str() {
            Some(url) => Ok(url.to_string()),
            None => Err(PebblesError::PhrasingError(PebblesErrorDetails::new(
                "Json value is not a string",
            ))),
        }
    }
    // endregion: Navigation

    // region: Command Contexts
    pub fn close(&self) -> Result<(), PebblesError> {
        self.service.delete("/window")?;
        Ok(())
    }

    pub fn maximize(&self) -> Result<(), PebblesError> {
        self.service.post("/window/maximize", &json!({}))?;
        Ok(())
    }

    pub fn minimize(&self) -> Result<(), PebblesError> {
        self.service.post("/window/minimize", &json!({}))?;
        Ok(())
    }
    // endregion: Command Contexts

    // region: Elements
    pub fn query(&self, identifier: &str) -> Result<Vec<WebElement>, PebblesError> {
        let body = if identifier.starts_with("$") {
            json!({"using": "xpath", "value": &identifier[1..]})
        } else {
            json!({"using": "css selector", "value": identifier})
        };

        let all_elements = self.service.post("/elements", &body)?.json::<Value>()?["value"]
            .as_array()
            .ok_or_else(|| {
                PebblesError::PhrasingError(PebblesErrorDetails::new(
                    "Error Fetching Elements from json.",
                ))
            })?
            .clone();
        

        if all_elements.len() == 0 {
            return Err(PebblesError::NoElementsError(PebblesErrorDetails::new(
                "No elements found.",
            )));
        }
        
        let elements_vec: Vec<WebElement> = all_elements
            .iter()
            .filter_map(|element| element["element-6066-11e4-a52e-4f735466cecf"].as_str())
            .map(|value| WebElement::new(self.service.clone(), value))
            .collect();

        Ok(elements_vec)
    }
    // endregion: Elements

    pub fn actions(&self, actions: &Value) -> Result<(), PebblesError> {
        self.service.post("/actions", actions)?;
        Ok(())
    }

    pub fn java_script(&self, script: &str, arguments: &[Value]) -> Result<Value, PebblesError> {
        Ok(self
            .service
            .post(
                "/execute/sync",
                &json!({"script": script, "args": arguments}),
            )?
            .json::<Value>()?["value"]
            .clone())
    }
}