use thiserror::Error;
use crate::xpath::{
self, xpath_item_set::XpathItemSet, ExpressionApplyError, ExpressionParseError, XpathItemTree,
};
use super::{
grammar::data_model::{AttributeNode, ElementNode, XpathItem},
Xpath,
};
impl Xpath {
pub fn find_attributes<'tree>(
&self,
tree: &'tree XpathItemTree,
) -> Result<Vec<&'tree AttributeNode>, ExpressionApplyError> {
let items = self.apply(tree)?;
Ok(items
.iter()
.filter_map(|item| item.as_node().ok())
.filter_map(|node| node.as_attribute_node().ok())
.collect())
}
pub fn find_elements<'tree>(
&self,
tree: &'tree XpathItemTree,
) -> Result<Vec<&'tree ElementNode>, ExpressionApplyError> {
let items = self.apply(tree)?;
Ok(items
.iter()
.filter_map(|item| item.as_node().ok())
.filter_map(|node| node.as_element_node().ok())
.collect())
}
pub fn find_elements_from_item<'tree>(
&self,
tree: &'tree XpathItemTree,
item: XpathItem<'tree>,
) -> Result<Vec<&'tree ElementNode>, ExpressionApplyError> {
let items = self.apply_to_item(tree, item)?;
Ok(items
.iter()
.filter_map(|item| item.as_node().ok())
.filter_map(|node| node.as_element_node().ok())
.collect())
}
pub fn find_elements_from_element<'tree>(
&self,
tree: &'tree XpathItemTree,
element: &'tree ElementNode,
) -> Result<Vec<&'tree ElementNode>, ExpressionApplyError> {
let item = element.to_item(tree);
self.find_elements_from_item(tree, item)
}
}
#[derive(PartialEq, Debug, Error)]
pub enum ParseApplyError {
#[error("Failed to parse xpath: {0}")]
ParseError(#[from] ExpressionParseError),
#[error("Failed to apply xpath: {0}")]
ApplyError(#[from] ExpressionApplyError),
}
pub fn find<'tree>(
tree: &'tree XpathItemTree,
xpath: &str,
) -> Result<XpathItemSet<'tree>, ParseApplyError> {
let xpath = xpath::parse(xpath)?;
Ok(xpath.apply(tree)?)
}
pub fn find_attributes<'tree>(
tree: &'tree XpathItemTree,
xpath: &str,
) -> Result<Vec<&'tree AttributeNode>, ParseApplyError> {
let xpath = xpath::parse(xpath)?;
Ok(xpath.find_attributes(tree)?)
}
pub fn find_elements<'tree>(
tree: &'tree XpathItemTree,
xpath: &str,
) -> Result<Vec<&'tree ElementNode>, ParseApplyError> {
let xpath = xpath::parse(xpath)?;
Ok(xpath.find_elements(tree)?)
}
#[cfg(test)]
mod tests {
use crate::html;
use super::*;
#[test]
fn find_attributes_should_find_attribute() {
let html = r#"<html><body><div id="example">Example 1</div></body></html>"#;
let tree = html::parse(html).unwrap();
let attributes = find_attributes(&tree, "//div/@id").unwrap();
let attribute = attributes[0];
assert_eq!(attribute.name, "id");
assert_eq!(attribute.value, "example");
}
#[test]
fn find_elements_should_find_element() {
let html = r#"<html><body><div id="example">Example 1</div></body></html>"#;
let tree = html::parse(html).unwrap();
let elements = find_elements(&tree, "//div").unwrap();
let element = elements[0];
assert_eq!(element.name, "div");
}
#[test]
fn find_elements_from_item_should_find_element() {
let html = r#"<html><body><div id="example">Example 1</div></body></html>"#;
let tree = html::parse(html).unwrap();
let items = find(&tree, "//body").unwrap();
let xpath = xpath::parse("//div").unwrap();
let elements = xpath
.find_elements_from_item(&tree, items[0].clone())
.unwrap();
let element = elements[0];
assert_eq!(element.name, "div");
}
#[test]
fn find_elements_from_element_should_find_element() {
let html = r#"<html><body><div id="example">Example 1</div></body></html>"#;
let tree = html::parse(html).unwrap();
let first_elements = find_elements(&tree, "//body").unwrap();
let xpath = xpath::parse("//div").unwrap();
let elements = xpath
.find_elements_from_element(&tree, first_elements[0])
.unwrap();
let element = elements[0];
assert_eq!(element.name, "div");
}
}