use super::ast::{
AttributeOperator, AttributeSelector, CaseSensitivity, Combinator, ComplexSelector, Compound,
LocalName, Nth, NthType, Selector, SelectorPart,
};
#[inline]
fn debug_assert_ident(kind: &str, value: &str) {
debug_assert!(
!value.is_empty() && !value.bytes().any(|b| b.is_ascii_whitespace()),
"{kind} must be a non-empty literal without whitespace, got {value:?} \
(use `parse` for a selector fragment)"
);
}
impl Selector {
#[must_use]
pub fn tag(name: impl AsRef<str>) -> Self {
Self::of(Compound::tag(name))
}
#[must_use]
pub fn class(name: impl AsRef<str>) -> Self {
Self::of(Compound::class(name))
}
#[must_use]
pub fn id(name: impl AsRef<str>) -> Self {
Self::of(Compound::id(name))
}
#[must_use]
pub fn any() -> Self {
Self::of(Compound::any())
}
#[must_use]
pub fn of(compound: Compound) -> Self {
Self {
selectors: vec![ComplexSelector {
parts: vec![SelectorPart {
combinator: None,
compound,
}],
}],
}
}
#[must_use]
pub fn child(self, compound: Compound) -> Self {
self.combine(Combinator::Child, compound)
}
#[must_use]
pub fn descendant(self, compound: Compound) -> Self {
self.combine(Combinator::Descendant, compound)
}
#[must_use]
pub fn or(mut self, other: Self) -> Self {
self.selectors.extend(other.selectors);
self
}
fn combine(mut self, combinator: Combinator, compound: Compound) -> Self {
if let Some(complex) = self.selectors.last_mut() {
complex.parts.push(SelectorPart {
combinator: Some(combinator),
compound,
});
}
self
}
}
impl Compound {
#[must_use]
pub fn tag(name: impl AsRef<str>) -> Self {
let name = name.as_ref();
debug_assert_ident("tag name", name);
Self {
name: Some(LocalName::new(name)),
..Self::default()
}
}
#[must_use]
pub fn any() -> Self {
Self {
explicit_universal: true,
..Self::default()
}
}
#[must_use]
pub fn class(name: impl AsRef<str>) -> Self {
Self::default().with_class(name)
}
#[must_use]
pub fn id(name: impl AsRef<str>) -> Self {
Self::default().with_id(name)
}
#[must_use]
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
let id = id.as_ref();
debug_assert_ident("id", id);
self.id = Some(id.into());
self
}
#[must_use]
pub fn with_class(mut self, class: impl AsRef<str>) -> Self {
let class = class.as_ref();
debug_assert_ident("class", class);
self.classes.push(class.into());
self
}
#[must_use]
pub fn with_attr(self, name: impl AsRef<str>) -> Self {
self.push_attr(name, None, "", CaseSensitivity::CaseSensitive)
}
#[must_use]
pub fn with_attr_eq(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
self.push_attr(
name,
Some(AttributeOperator::Equals),
value.as_ref(),
CaseSensitivity::CaseSensitive,
)
}
#[must_use]
pub fn with_attr_eq_ignore_case(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
self.push_attr(
name,
Some(AttributeOperator::Equals),
value.as_ref(),
CaseSensitivity::AsciiCaseInsensitive,
)
}
#[must_use]
pub fn with_attr_includes(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
self.push_attr(
name,
Some(AttributeOperator::Includes),
value.as_ref(),
CaseSensitivity::CaseSensitive,
)
}
#[must_use]
pub fn with_attr_dash_match(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
self.push_attr(
name,
Some(AttributeOperator::DashMatch),
value.as_ref(),
CaseSensitivity::CaseSensitive,
)
}
#[must_use]
pub fn with_attr_prefix(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
self.push_attr(
name,
Some(AttributeOperator::Prefix),
value.as_ref(),
CaseSensitivity::CaseSensitive,
)
}
#[must_use]
pub fn with_attr_suffix(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
self.push_attr(
name,
Some(AttributeOperator::Suffix),
value.as_ref(),
CaseSensitivity::CaseSensitive,
)
}
#[must_use]
pub fn with_attr_substring(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
self.push_attr(
name,
Some(AttributeOperator::Substring),
value.as_ref(),
CaseSensitivity::CaseSensitive,
)
}
#[must_use]
pub fn with_nth_child(mut self, a: i32, b: i32) -> Self {
self.nth.push(Nth {
ty: NthType::Child,
a,
b,
});
self
}
#[must_use]
pub fn with_nth_of_type(mut self, a: i32, b: i32) -> Self {
self.nth.push(Nth {
ty: NthType::OfType,
a,
b,
});
self
}
#[must_use]
pub fn with_first_child(self) -> Self {
self.with_nth_child(0, 1)
}
#[must_use]
pub fn with_first_of_type(self) -> Self {
self.with_nth_of_type(0, 1)
}
#[must_use]
pub fn without(mut self, compound: Self) -> Self {
self.negations.push(compound);
self
}
fn push_attr(
mut self,
name: impl AsRef<str>,
operator: Option<AttributeOperator>,
value: &str,
case: CaseSensitivity,
) -> Self {
let name = name.as_ref();
debug_assert_ident("attribute name", name);
self.attributes.push(AttributeSelector {
name: name.to_ascii_lowercase().into(),
operator,
value: value.into(),
case,
});
self
}
}