use crate::css::Value;
use crate::sass::SassString;
use crate::value::{ListSeparator, Quotes};
use crate::{Error, ParseError, ScopeRef};
use std::fmt;
use std::io::Write;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
pub struct Selectors {
pub s: Vec<Selector>,
backref: Selector,
}
impl Selectors {
pub fn root() -> Self {
Selectors::new(vec![Selector::root()])
}
pub fn new(s: Vec<Selector>) -> Self {
Selectors {
s,
backref: Selector::root(),
}
}
pub fn one(&self) -> Selector {
self.s.first().cloned().unwrap_or_else(Selector::root)
}
pub fn inside(&self, parent: &Self) -> Self {
let mut result = Vec::new();
for p in &parent.s {
for s in &self.s {
result.push(p.join(s, &parent.backref));
}
}
Selectors {
s: result,
backref: parent.backref.clone(),
}
}
pub fn with_backref(self, context: Selector) -> Self {
self.inside(&Selectors {
s: vec![Selector::root()],
backref: context,
})
}
pub fn to_value(&self) -> Value {
if self.s.len() == 1 && self.s[0].0.is_empty() {
return Value::Null;
}
let content = self
.s
.iter()
.map(|s: &Selector| {
Value::List(
s.to_string()
.split_whitespace()
.map(|p| Value::Literal(p.to_string(), Quotes::None))
.collect(),
ListSeparator::Space,
false,
)
})
.collect::<Vec<_>>();
let sep = if content.len() == 1 {
ListSeparator::Space
} else {
ListSeparator::Comma
};
Value::List(content, sep, false)
}
pub fn eval(&self, scope: ScopeRef) -> Result<Selectors, Error> {
let s = Selectors::new(
self.s
.iter()
.map(|s| s.eval(scope.clone()))
.collect::<Result<Vec<_>, Error>>()?,
);
use crate::parser::code_span;
use crate::parser::selectors::selectors;
Ok(ParseError::check(selectors(code_span(
format!("{} ", s).as_bytes(),
)))?)
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
pub struct Selector(pub Vec<SelectorPart>);
impl Selector {
pub fn root() -> Self {
Selector(vec![])
}
fn join(&self, other: &Selector, alt_context: &Selector) -> Selector {
if other.0.iter().any(|p| p == &SelectorPart::BackRef) {
let mut result = Vec::new();
for p in &other.0 {
if p == &SelectorPart::BackRef {
if self.0.is_empty() {
result.extend(alt_context.0.iter().cloned());
} else {
result.extend(self.0.iter().cloned());
}
} else {
result.push(p.clone())
}
}
Selector(result)
} else {
let mut result = self.0.clone();
if !result.is_empty()
&& !other.0.first().map(|p| p.is_operator()).unwrap_or(false)
{
result.push(SelectorPart::Descendant);
}
result.extend(other.0.iter().cloned());
Selector(result)
}
}
fn eval(&self, scope: ScopeRef) -> Result<Selector, Error> {
self.0
.iter()
.map(|sp| sp.eval(scope.clone()))
.collect::<Result<_, _>>()
.map(Selector)
}
}
#[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 is_operator(&self) -> bool {
match *self {
SelectorPart::Descendant | SelectorPart::RelOp(_) => true,
SelectorPart::Simple(_)
| SelectorPart::Attribute { .. }
| SelectorPart::PseudoElement { .. }
| SelectorPart::Pseudo { .. }
| SelectorPart::BackRef => false,
}
}
fn eval(&self, scope: ScopeRef) -> Result<SelectorPart, Error> {
match *self {
SelectorPart::Attribute {
ref name,
ref op,
ref val,
ref modifier,
} => Ok(SelectorPart::Attribute {
name: name.evaluate2(scope.clone())?,
op: op.clone(),
val: val.evaluate_opt_unquote(scope)?,
modifier: *modifier,
}),
SelectorPart::Simple(ref v) => {
Ok(SelectorPart::Simple(v.evaluate2(scope)?))
}
SelectorPart::Pseudo { ref name, ref arg } => {
let arg = match &arg {
Some(ref a) => Some(a.eval(scope.clone())?),
None => None,
};
Ok(SelectorPart::Pseudo {
name: name.evaluate2(scope)?,
arg,
})
}
SelectorPart::PseudoElement { ref name, ref arg } => {
let arg = match &arg {
Some(ref a) => Some(a.eval(scope.clone())?),
None => None,
};
Ok(SelectorPart::PseudoElement {
name: name.evaluate2(scope)?,
arg,
})
}
ref sp => Ok(sp.clone()),
}
}
}
impl fmt::Display for Selectors {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
if let Some((first, rest)) = self.s.split_first() {
first.fmt(out)?;
let separator = if out.alternate() { "," } else { ", " };
for item in rest {
out.write_str(separator)?;
item.fmt(out)?;
}
}
Ok(())
}
}
impl fmt::Display for Selector {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
let mut buf = vec![];
for p in &self.0 {
if out.alternate() {
write!(&mut buf, "{:#}", p).map_err(|_| fmt::Error)?;
} else {
write!(&mut buf, "{}", p).map_err(|_| fmt::Error)?;
}
}
if buf.ends_with(b"> ") {
buf.pop();
}
while buf.first() == Some(&b' ') {
buf.remove(0);
}
let buf = String::from_utf8(buf).map_err(|_| fmt::Error)?;
out.write_str(&buf.replace(" ", " "))
}
}
impl fmt::Display for SelectorPart {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
match *self {
SelectorPart::Simple(ref s) => write!(out, "{}", s),
SelectorPart::Descendant => write!(out, " "),
SelectorPart::RelOp(ref c) => {
if out.alternate() && *c != b'~' {
write!(out, "{}", *c as char)
} else {
write!(out, " {} ", *c as char)
}
}
SelectorPart::Attribute {
ref name,
ref op,
ref val,
ref modifier,
} => write!(
out,
"[{}{}{}{}]",
name,
op,
val,
modifier.map(|m| format!(" {}", m)).unwrap_or_default()
),
SelectorPart::PseudoElement { ref name, ref arg } => {
write!(out, "::{}", name)?;
if let Some(ref arg) = *arg {
if out.alternate() {
write!(out, "({:#})", arg)?
} else {
write!(out, "({})", arg)?
}
}
Ok(())
}
SelectorPart::Pseudo { ref name, ref arg } => {
let name = format!("{}", name);
if let Some(ref arg) = *arg {
if out.alternate()
|| name == "nth-child"
|| name == "nth-of-type"
{
write!(out, ":{}({:#})", name, arg)
} else {
write!(out, ":{}({})", name, arg)
}
} else {
write!(out, ":{}", name)
}
}
SelectorPart::BackRef => write!(out, "&"),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn root_join() {
let s = Selector(vec![SelectorPart::Simple("foo".into())]);
assert_eq!(Selector::root().join(&s, &Selector::root()), s)
}
#[test]
fn simple_join() {
let s = Selector(vec![SelectorPart::Simple("foo".into())]).join(
&Selector(vec![SelectorPart::Simple(".bar".into())]),
&Selector::root(),
);
assert_eq!(format!("{}", s), "foo .bar")
}
#[test]
fn backref_join() {
let s = Selector(vec![SelectorPart::Simple("foo".into())]).join(
&Selector(vec![
SelectorPart::BackRef,
SelectorPart::Simple(".bar".into()),
]),
&Selector::root(),
);
assert_eq!(format!("{}", s), "foo.bar")
}
}