pub(crate) mod parser;
use crate::plugins::Plugin;
use std::{borrow::Cow, cmp::Ordering};
pub(crate) use parser::parse;
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub enum Modifier<'a> {
Builtin {
is_negative: bool,
value: &'a str,
},
Arbitrary {
prefix: &'a str,
hint: &'a str,
value: Cow<'a, str>,
},
}
#[derive(Debug, Clone, Eq)]
pub struct Variant<'a> {
pub(crate) order: usize,
pub(crate) prefixed: bool,
pub(crate) template: Cow<'a, str>,
}
impl<'a> Variant<'a> {
pub(crate) const fn new_const(counter: &mut usize, template: &'static str) -> Self {
*counter += 1;
Self {
order: *counter - 1,
prefixed: false,
template: Cow::Borrowed(template),
}
}
pub fn new<T: Into<Cow<'a, str>>>(order: usize, template: T) -> Self {
Self {
order,
prefixed: false,
template: template.into(),
}
}
#[must_use]
pub const fn with_prefixed(mut self) -> Self {
self.prefixed = true;
self
}
}
impl PartialEq for Variant<'_> {
fn eq(&self, other: &Self) -> bool {
self.template == other.template
}
}
#[derive(Clone, Debug)]
pub(crate) struct Selector<'a> {
pub(crate) layer: i8,
pub(crate) order: usize,
pub(crate) full: &'a str,
pub(crate) modifier: Modifier<'a>,
pub(crate) variants: Vec<Variant<'a>>,
pub(crate) is_important: bool,
pub(crate) plugin: &'static (dyn Plugin + Sync + Send),
}
impl PartialEq for Selector<'_> {
fn eq(&self, other: &Self) -> bool {
self.full == other.full
&& self.modifier == other.modifier
&& self.variants == other.variants
&& self.is_important == other.is_important
&& self.layer == other.layer
}
}
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) -> Ordering {
if self.order == other.order && self == other {
return Ordering::Equal;
}
self.layer.cmp(&other.layer).then_with(|| {
if self.variants.is_empty() && !other.variants.is_empty() {
Ordering::Less
} else if !self.variants.is_empty() && other.variants.is_empty() {
Ordering::Greater
} else if !self.variants.is_empty() && !other.variants.is_empty() {
let mut compared = None;
for variant_i in 0..self.variants.len() {
if variant_i >= other.variants.len() {
compared = Some(Ordering::Greater);
break;
}
let res = self
.variants
.get(variant_i)
.as_ref()
.unwrap()
.order
.cmp(&other.variants.get(variant_i).unwrap().order);
if res != Ordering::Equal {
compared = Some(res);
break;
}
}
compared.unwrap_or(Ordering::Less).then_with(|| {
self.order.cmp(&other.order).then_with(|| {
self.full
.cmp(other.full)
.then_with(|| self.modifier.cmp(&other.modifier))
})
})
} else {
self.order.cmp(&other.order).then_with(|| {
self.full
.cmp(other.full)
.then_with(|| self.modifier.cmp(&other.modifier))
})
}
})
}
}
#[cfg(test)]
mod tests {
use crate::{config::Config, selector::parse};
use std::collections::BTreeSet;
#[test]
fn sorting_test() {
let config = Config::default();
let selectors1 = parse(
"lg:bg-red-500",
None,
None,
&config,
&config.get_derived_variants(),
);
let selectors2 = parse(
"bg-red-500",
None,
None,
&config,
&config.get_derived_variants(),
);
let mut selectors = BTreeSet::new();
selectors.insert(selectors1[0].as_ref().unwrap());
selectors.insert(selectors2[0].as_ref().unwrap());
let mut iter = selectors.iter();
assert!(
iter.next().unwrap().full == "bg-red-500"
&& iter.next().unwrap().full == "lg:bg-red-500"
);
}
#[test]
fn layers_test() {
let config = Config::default();
let selectors1 = parse(
"lg:bg-red-500",
None,
None,
&config,
&config.get_derived_variants(),
);
let mut selectors2 = parse(
"bg-red-500",
None,
None,
&config,
&config.get_derived_variants(),
);
selectors2[0].as_mut().unwrap().layer = 42;
let mut selectors = BTreeSet::new();
selectors.insert(selectors1[0].as_ref().unwrap());
selectors.insert(selectors2[0].as_ref().unwrap());
let mut iter = selectors.iter();
assert!(
iter.next().unwrap().full == "lg:bg-red-500"
&& iter.next().unwrap().full == "bg-red-500"
);
}
}