Skip to content

Commit

Permalink
Merge branch 'feature/selection_filter'
Browse files Browse the repository at this point in the history
  • Loading branch information
niklak committed Oct 28, 2024
2 parents 12354da + 191f47e commit 88569ee
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 36 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes to the `dom_query` crate will be documented in this file.

### Added
- Added `Selection::filter` , `Selection::filter_matcher` and `Selection::try_filter` methods that filter a current selection.
- Added `Selection::filter_selection` method that filters a current selection with another selection.

## [0.7.0] - 2024-10-27

Expand Down
4 changes: 3 additions & 1 deletion src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,9 @@ impl TreeSink for Document {
/// Should never be called on a non-element node; Feel free to `panic!`.
#[inline]
fn elem_name(&self, target: &Self::Handle) -> Self::ElemName<'_> {
self.tree.get_name(target).expect("target node is not an element!")
self.tree
.get_name(target)
.expect("target node is not an element!")
}

/// Create an element.
Expand Down
11 changes: 6 additions & 5 deletions src/dom_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,17 @@ impl Tree<NodeData> {
NodeRef { id, tree: self }
}

/// Gets node's name by by id
pub fn get_name<'a>(&'a self, id: &NodeId) -> Option<Ref<'a, QualName>> {
/// Gets node's name by by id
pub fn get_name<'a>(&'a self, id: &NodeId) -> Option<Ref<'a, QualName>> {
Ref::filter_map(self.nodes.borrow(), |nodes| {
let node = nodes.get(id.value)?;
if let NodeData::Element(ref el) = node.data {
Some(&el.name)
}else{
None
} else {
None
}
}).ok()
})
.ok()
}
}

Expand Down
1 change: 0 additions & 1 deletion src/entities.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#[cfg(feature = "hashbrown")]
mod inline {
use hashbrown::HashSet;
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@
//! assert_eq!(text.to_string(), "Test Page");
//!
//! ```
//!
//!
//! ## Accessing immediate text
//!
//! ```
Expand Down
59 changes: 39 additions & 20 deletions src/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,25 +203,25 @@ impl<'a> Selection<'a> {

/// Checks the current matches set of elements against a selection and
/// returns true if at least one of these elements matches.
pub fn is_selection(&self, sel: &Selection) -> bool {
if self.is_empty() || sel.is_empty() {
pub fn is_selection(&self, other: &Selection) -> bool {
if self.is_empty() || other.is_empty() {
return false;
}
let m: Vec<usize> = sel.nodes().iter().map(|node| node.id.value).collect();
let m: Vec<usize> = other.nodes().iter().map(|node| node.id.value).collect();
self.nodes().iter().any(|node| m.contains(&node.id.value))
}

/// Filters the current set of matched elements to those that match the
/// given CSS selector.
///
///
/// # Panics
///
///
/// # Arguments
///
///
/// * `sel` - The CSS selector to match against.
///
///
/// # Returns
///
///
/// A new Selection object containing the matched elements.
pub fn filter(&self, sel: &str) -> Selection<'a> {
if self.is_empty() {
Expand All @@ -231,15 +231,15 @@ impl<'a> Selection<'a> {
self.filter_matcher(&matcher)
}

/// Filters the current set of matched elements to those that match the
/// Reduces the current set of matched elements to those that match the
/// given CSS selector.
///
///
/// # Arguments
///
///
/// * `sel` - The CSS selector to match against.
///
///
/// # Returns
///
///
/// `None` if the selector was invalid, otherwise a new `Selection` object containing the matched elements.
pub fn try_filter(&self, sel: &str) -> Option<Selection<'a>> {
if self.is_empty() {
Expand All @@ -248,25 +248,44 @@ impl<'a> Selection<'a> {
Matcher::new(sel).ok().map(|m| self.filter_matcher(&m))
}

/// Filters the current set of matched elements to those that match the
/// Reduces the current set of matched elements to those that match the
/// given matcher.
///
///
/// # Arguments
///
///
/// * `matcher` - The matcher to match against.
///
///
/// # Returns
///
///
/// A new Selection object containing the matched elements.
pub fn filter_matcher(&self, matcher: &Matcher) -> Selection<'a> {
if self.is_empty() {
return self.clone();
}
let nodes = self.nodes().iter()
.filter(|&node| matcher.match_element(node)).cloned().collect();
let nodes = self
.nodes()
.iter()
.filter(|&node| matcher.match_element(node))
.cloned()
.collect();
Selection { nodes }
}

/// Reduces the set of matched elements to those that match a node in the specified `Selection`.
/// It returns a new `Selection` for this subset of elements.
pub fn filter_selection(&self, other: &Selection) -> Selection<'a> {
if self.is_empty() || other.is_empty() {
return self.clone();
}
let m: Vec<usize> = other.nodes().iter().map(|node| node.id.value).collect();
let nodes = self
.nodes()
.iter()
.filter(|&node| m.contains(&node.id.value))
.cloned()
.collect();
Selection { nodes }
}
}

//manipulating methods
Expand Down
2 changes: 1 addition & 1 deletion tests/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ pub static HEADING_CONTENTS: &str = r#"<!DOCTYPE html>
<p>This is a test page contents.</p>
</div
</body>
</html>"#;
</html>"#;
2 changes: 0 additions & 2 deletions tests/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ mod alloc;
mod data;
use data::HEADING_CONTENTS;



#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn parse_doc_str() {
Expand Down
37 changes: 32 additions & 5 deletions tests/selection-query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ use wasm_bindgen_test::*;

mod alloc;



#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn test_is() {
Expand Down Expand Up @@ -56,7 +54,6 @@ fn test_is_selection_not() {
);
}


#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn test_filter_selection() {
Expand All @@ -74,7 +71,6 @@ fn test_filter_selection() {
assert!(sel.select("h1").exists());
}


#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn test_try_filter_selection() {
Expand All @@ -85,4 +81,35 @@ fn test_try_filter_selection() {
let filtered_sel = sel.try_filter("div.text-content").unwrap();
assert!(!filtered_sel.select("h1").exists());
assert!(sel.select("h1").exists());
}
}

#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn test_filter_selection_other() {
let doc: Document = r#"<!DOCTYPE html>
<html lang="en">
<head>TEST</head>
<body>
<div class="content">
<p>Content text has a <a href="/0">link</a></p>
</div>
<footer>
<a href="/1">Footer Link</a>
</footer>
</body>
</html>
"#
.into();

// selecting all links in the document
let sel_with_links = doc.select("a[href]");

assert_eq!(sel_with_links.length(), 2);
// selecting every element inside `.content`
let content_sel = doc.select("div.content *");

// filter selection by content selection, so now we get only links (actually only 1 link) that are inside `.content`
let filtered_sel = sel_with_links.filter_selection(&content_sel);

assert_eq!(filtered_sel.length(), 1);
}

0 comments on commit 88569ee

Please sign in to comment.