use crate::css::{self, parser::selector_set};
use crate::parser::input_span;
use crate::sass::SassString;
use crate::{Error, ParseError, ScopeRef};
use std::fmt::Write;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
pub struct Selectors {
s: Vec<Selector>,
}
impl Selectors {
pub fn root() -> Self {
Self::new(vec![Selector::root()])
}
pub fn is_root(&self) -> bool {
self.s == [Selector::root()]
}
pub fn new(s: Vec<Selector>) -> Self {
Self { s }
}
pub fn eval(&self, scope: ScopeRef) -> Result<css::SelectorSet, Error> {
let mut s = Vec::new();
for sel in &self.s {
s.extend(sel.eval(scope.clone())?);
}
Ok(css::SelectorSet { s })
}
fn write_eval(
&self,
f: &mut String,
scope: ScopeRef,
) -> Result<(), Error> {
if let Some((first, rest)) = self.s.split_first() {
first.write_eval(f, scope.clone())?;
for s in rest {
f.push_str(", ");
s.write_eval(f, scope.clone())?;
}
}
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
pub struct Selector(Vec<SelectorPart>);
impl Selector {
pub fn root() -> Self {
Self(vec![])
}
pub fn new(s: Vec<SelectorPart>) -> Self {
Self(s)
}
fn eval(&self, scope: ScopeRef) -> Result<Vec<css::Selector>, Error> {
if self.0.is_empty() {
Ok(vec![css::Selector::default()])
} else {
let mut text = String::new();
self.write_eval(&mut text, scope)?;
Ok(ParseError::check(selector_set(input_span(text).borrow()))?.s)
}
}
fn write_eval(
&self,
f: &mut String,
scope: ScopeRef,
) -> Result<(), Error> {
for p in &self.0 {
p.write_eval(f, scope.clone())?;
}
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
pub enum SelectorPart {
Simple(SassString),
Descendant,
RelOp(u8),
Attribute {
name: SassString,
op: String,
val: SassString,
modifier: Option<char>,
},
PseudoElement {
name: SassString,
arg: Option<Selectors>,
},
Pseudo {
name: SassString,
arg: Option<Selectors>,
},
BackRef,
}
impl SelectorPart {
fn write_eval(
&self,
f: &mut String,
scope: ScopeRef,
) -> Result<(), Error> {
match self {
SelectorPart::Simple(s) => we(s, f, scope)?,
SelectorPart::Descendant => f.push(' '),
SelectorPart::RelOp(op) => {
if let Some(ch) = char::from_u32(u32::from(*op)) {
f.push(' ');
f.push(ch);
f.push(' ');
}
}
SelectorPart::Attribute {
name,
op,
val,
modifier,
} => {
f.push('[');
we(name, f, scope.clone())?;
f.push_str(op);
let val = val.evaluate(scope)?.opt_unquote();
write!(f, "{val}")?;
if let Some(modifier) = modifier {
if val.quotes().is_none() {
f.push(' ');
}
f.push(*modifier);
}
f.push(']');
}
SelectorPart::PseudoElement { name, arg } => {
f.push_str("::");
we(name, f, scope.clone())?;
if let Some(arg) = arg {
f.push('(');
arg.write_eval(f, scope)?;
f.push(')');
}
}
SelectorPart::Pseudo { name, arg } => {
f.push(':');
we(name, f, scope.clone())?;
if let Some(arg) = arg {
f.push('(');
arg.write_eval(f, scope)?;
f.push(')');
}
}
SelectorPart::BackRef => f.push('&'),
}
Ok(())
}
}
fn we(s: &SassString, f: &mut String, scope: ScopeRef) -> Result<(), Error> {
write!(f, "{}", s.evaluate(scope)?)?;
Ok(())
}