use crate::page::Page;
use async_trait::async_trait;
use regex::Regex;
use rustc_hash::FxHashMap as HashMap;
use std::sync::Arc;
#[macro_use]
mod macros;
mod registry;
pub mod assertion;
pub mod cookie;
pub mod interaction;
pub mod javascript;
pub mod navigation;
pub mod screenshot;
pub mod storage;
pub mod variable;
pub mod wait;
pub use registry::StepRegistry;
#[async_trait]
pub trait StepDef: Send + Sync {
fn description(&self) -> &'static str;
fn category(&self) -> StepCategory;
fn example(&self) -> &'static str;
fn pattern(&self) -> &Regex;
async fn execute(
&self,
page: &Arc<Page>,
caps: ®ex::Captures<'_>,
data_table: Option<&[Vec<String>]>,
vars: &mut HashMap<String, String>,
) -> crate::error::Result<Option<serde_json::Value>>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize)]
pub enum StepCategory {
Navigation,
Interaction,
Wait,
Assertion,
Variable,
Cookie,
Storage,
Screenshot,
JavaScript,
}
#[must_use]
pub fn q(s: &str) -> String {
let s = s.trim();
if (s.starts_with('"') && s.ends_with('"')) || (s.starts_with('\'') && s.ends_with('\'')) {
s[1..s.len() - 1].to_string()
} else {
s.to_string()
}
}
#[must_use]
pub fn js_escape(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 8);
for c in s.chars() {
match c {
'\\' => out.push_str("\\\\"),
'\'' => out.push_str("\\'"),
'"' => out.push_str("\\\""),
'`' => out.push_str("\\`"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
'\0' => out.push_str("\\0"),
'\u{2028}' => out.push_str("\\u2028"),
'\u{2029}' => out.push_str("\\u2029"),
_ => out.push(c),
}
}
out
}
pub async fn find(page: &Arc<Page>, selector: &str) -> crate::error::Result<crate::backend::AnyElement> {
let inner = page.inner();
if crate::selectors::is_rich_selector(selector) {
crate::selectors::query_one(inner, selector, false, None).await
} else {
inner.find_element(selector).await
}
}