use bevy::utils::AHasher;
use cssparser::CowRcStr;
use smallvec::{smallvec, SmallVec};
use std::{
cmp::Ordering,
hash::{Hash, Hasher},
sync::Mutex,
};
static RULE_COUNTER: Mutex<usize> = Mutex::new(0);
#[derive(Clone, Debug)]
#[derive(PartialEq, Eq)]
#[derive(PartialOrd, Ord)]
#[derive(Hash)]
pub enum SelectorElement
{
Name(String),
Component(String),
Class(String),
#[cfg(feature = "pseudo_class")]
PseudoClass(String),
#[cfg(feature = "pseudo_prop")]
PseudoProp(String),
Child,
}
#[derive(Clone, Debug, Default)]
pub struct Selector
{
hash: u64,
elements: SmallVec<[SelectorElement; 8]>,
load_order: usize,
}
impl Selector
{
pub fn new(
elements: SmallVec<[SelectorElement; 8]>
) -> Self {
let hasher = AHasher::default();
let hasher = elements.iter().fold(hasher, |mut hasher, el|
{
el.hash(&mut hasher);
hasher
});
let hash = hasher.finish();
Self{
elements,
hash,
load_order: RULE_COUNTER
.lock()
.map(|mut lock|
{
*lock += 1;
*lock
})
.unwrap_or_default(),
}
}
pub fn get_parent_tree(
&self
) -> SmallVec<[SmallVec<[&SelectorElement; 8]>; 8]> {
let mut tree = SmallVec::new();
let mut current_level = SmallVec::new();
for element in &self.elements
{
match element
{
SelectorElement::Child => {
tree.push(current_level);
current_level = SmallVec::new();
}
_ => current_level.push(element),
}
}
tree.push(current_level);
tree
}
}
impl std::fmt::Display
for Selector
{
fn fmt(
&self,
formatter: &mut std::fmt::Formatter<'_>
) -> std::fmt::Result {
let mut buffer = String::new();
for element in &self.elements
{
match element
{
SelectorElement::Name(n) => {
buffer.push('#');
buffer.push_str(n);
}
SelectorElement::Component(c) => {
buffer.push_str(c);
}
SelectorElement::Class(c) => {
buffer.push('.');
buffer.push_str(c);
}
#[cfg(feature = "pseudo_class")]
SelectorElement::PseudoClass(c) => {
buffer.push(':');
buffer.push_str(c);
}
#[cfg(feature = "pseudo_prop")]
SelectorElement::PseudoProp(p) => {
buffer.push_str("::");
buffer.push_str(p);
}
SelectorElement::Child => {
buffer.push(' ');
}
}
}
write!(formatter, "{}", buffer)
}
}
impl PartialEq
for Selector
{
fn eq(
&self,
other: &Self
) -> bool {
self.hash == other.hash
}
}
impl Eq
for Selector
{
}
impl PartialOrd
for Selector
{
fn partial_cmp(
&self,
other: &Self
) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord
for Selector
{
fn cmp(
&self,
other: &Self
) -> std::cmp::Ordering {
match self.elements.len().cmp(&other.elements.len())
{
Ordering::Equal => self.load_order.cmp(&other.load_order),
not_eq => not_eq,
}
}
}
impl Hash
for Selector
{
fn hash<H: Hasher>(
&self,
state: &mut H
) {
self.hash.hash(state);
}
}
impl<'i> From<Vec<CowRcStr<'i>>>
for Selector
{
fn from(
input: Vec<CowRcStr<'i>>
) -> Self {
let mut elements = smallvec![];
let mut next_is_class = false;
for value in input.into_iter()
.filter(|v| !v.is_empty())
{
if value.as_ref() == "."
{
next_is_class = true;
continue;
}
if let Some(value) = value.strip_prefix('#')
{
elements.push(SelectorElement::Name(value.to_string()));
}
else if next_is_class
{
elements.push(SelectorElement::Class(value.to_string()))
}
else
{
elements.push(SelectorElement::Component(value.to_string()))
}
next_is_class = false;
}
Self::new(elements)
}
}