use css_cat::SelectorList;
use crate::document::Document;
use crate::error::Error;
use crate::matcher;
use crate::node::{Node, NodeId};
use crate::selector_parse::parse_selectors;
pub fn query_selector(doc: &Document, selector_source: &str) -> Result<Option<NodeId>, Error> {
let selectors = parse_selectors(selector_source)?;
let candidates = walk_document_order(doc, doc.root(), Vec::new());
Ok(candidates
.into_iter()
.find(|id| any_selector_matches(doc, *id, &selectors)))
}
pub fn query_selector_all(doc: &Document, selector_source: &str) -> Result<Vec<NodeId>, Error> {
let selectors = parse_selectors(selector_source)?;
let candidates = walk_document_order(doc, doc.root(), Vec::new());
Ok(candidates
.into_iter()
.filter(|id| any_selector_matches(doc, *id, &selectors))
.collect())
}
fn any_selector_matches(doc: &Document, id: NodeId, list: &SelectorList) -> bool {
list.selectors()
.iter()
.any(|sel| matcher::matches(doc, id, sel))
}
fn walk_document_order(doc: &Document, start: NodeId, acc: Vec<NodeId>) -> Vec<NodeId> {
let with_self = if matches!(doc.get(start), Some(Node::Element(_))) {
acc.into_iter().chain(std::iter::once(start)).collect()
} else {
acc
};
doc.get(start)
.map_or(&[][..], Node::children)
.iter()
.fold(with_self, |a, child| walk_document_order(doc, *child, a))
}