1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use crate::pipeline::{ScrapeContext, ScrapeError};
use json_dotpath::DotPaths;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter, Result as FormatResult};

pub type JsonValue = serde_json::Value;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Value {
    Constant(String),
    Context(String),
    ElementText,
    ElementAttribute(String),
}

impl Display for Value {
    fn fmt(&self, fmt: &mut Formatter<'_>) -> FormatResult {
        write!(fmt, "{:?}", self)
    }
}

impl Value {
    pub fn constant<T: Into<String>>(value: T) -> Self {
        Value::Constant(value.into())
    }

    pub fn context<T: Into<String>>(key: T) -> Self {
        Value::Context(key.into())
    }

    pub fn element_attribute<T: Into<String>>(attribute: T) -> Self {
        Value::ElementAttribute(attribute.into())
    }

    pub async fn resolve(&self, context: &mut ScrapeContext) -> Result<Option<String>, ScrapeError> {
        match self {
            Value::Constant(value) => Ok(Some(value.to_owned())),

            Value::Context(key) => context
                .values
                .dot_get::<JsonValue>(&key)
                .map(to_string)
                .map_err(|_| ScrapeError::ValueResolveError),

            Value::ElementText => {
                let element = context.current_element.as_mut().ok_or(ScrapeError::MissingElement)?;
                element
                    .text()
                    .await
                    .map(Option::Some)
                    .map_err(ScrapeError::WebdriverCommandError)
            }

            Value::ElementAttribute(attribute) => {
                let element = context.current_element.as_mut().ok_or(ScrapeError::MissingElement)?;
                element
                    .attr(attribute)
                    .await
                    .map_err(ScrapeError::WebdriverCommandError)
            }
        }
    }
}

fn to_string(value: Option<JsonValue>) -> Option<String> {
    value.map(|value| match value {
        JsonValue::String(value) => value.clone(),
        value => value.to_string(),
    })
}