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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
mod compound;
mod simple;
use crate::Element;
use self::{compound::CompoundSelector, simple::SimpleSelector};
/// Basic selector. It follows the
/// [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors)
/// standard, but not all rules are supported now. Please refer
/// to [`Selector::from`](Selector::from).
#[derive(Debug)]
pub struct Selector(Vec<CompoundSelector>);
impl Selector {
/// Check if the `element` matches the `selector`.
///
/// ```
/// use html_editor::{Node, Element};
/// use html_editor::operation::*;
///
/// let element: Element = Element::new(
/// "div",
/// vec![("id", "app")],
/// vec![Node::Text("Hello World!".to_string())],
/// );
///
/// let selector = Selector::from("div#app");
///
/// assert_eq!(selector.matches(&element), true);
/// ```
pub fn matches(&self, element: &Element) -> bool {
let element_classes = element
.attrs
.iter()
.find(|(key, _)| key == "class")
.map(|(_, v)| v.split(' ').map(|name| name.trim()).collect::<Vec<_>>());
let element_id = element
.attrs
.iter()
.find(|(key, _)| key == "id")
.map(|(_, v)| v);
self.0.iter().any(|compound_selector| {
compound_selector
.0
.iter()
.all(|simple_selector| match simple_selector {
SimpleSelector::Class(selector_class) => match &element_classes {
Some(element_classes) => element_classes
.iter()
.any(|element_class| element_class == selector_class),
None => false,
},
SimpleSelector::Id(selector_id) => match element_id {
Some(element_id) => element_id == selector_id,
None => false,
},
SimpleSelector::Tag(tag) => tag == &element.name,
})
})
}
}
impl From<&str> for Selector {
/// Generate a selector from given string, following the CSS
/// selector standard.
///
/// Not all rules are supported. Below shows the rules currently
/// supported:
///
/// ```
/// use html_editor::operation::Selector;
///
/// // Type Selector
/// Selector::from("span");
/// // Class selector
/// Selector::from(".class");
/// // ID selector
/// Selector::from("#id");
///
/// // Selector list
/// Selector::from("h1, h2");
/// // Compound selector
/// Selector::from("input.username");
///
/// // Disallowed input that may cause unexpected result
/// Selector::from("div span");
/// Selector::from("a[target=_blank]");
/// ```
fn from(selector: &str) -> Self {
Selector(selector.split(',').map(CompoundSelector::from).collect())
}
}