use super::compound::CompoundSelector;
use super::error::BadSelector0;
use super::{BadSelector, CssSelectorSet, Opt, SelectorSet};
use crate::css::Value;
use crate::output::CssBuf;
use crate::parser::input_span;
use crate::sass::CallError;
use crate::value::ListSeparator;
use crate::{Invalid, ParseError};
use core::fmt;
use std::iter::once;
type RelBox = Box<(RelKind, Selector)>;
#[derive(Default, Clone, PartialEq, Eq)]
pub struct Selector {
rel_of: Option<RelBox>,
compound: CompoundSelector,
}
impl Selector {
pub(crate) fn no_placeholder(&self) -> Opt<Self> {
let compound = match self.compound.no_placeholder() {
Opt::Some(compound) => compound,
Opt::Any => CompoundSelector::default(),
Opt::None => return Opt::None,
};
if self.is_local_empty() && self.rel_of.is_some() {
eprintln!("Deprecated dobule empty relation");
return Opt::None;
}
let rel_of = if let Some((kind, rel)) = self.rel_of.as_deref() {
match rel.no_placeholder() {
Opt::Some(rel) => Some(Box::new((*kind, rel))),
Opt::Any => None,
Opt::None => return Opt::None,
}
} else {
None
};
Opt::Some(Self { rel_of, compound })
}
pub(crate) fn no_leading_combinator(&self) -> Opt<Self> {
if self.has_leading_combinator() {
Opt::None
} else {
Opt::Some(self.clone())
}
}
fn has_leading_combinator(&self) -> bool {
self.rel_of.as_deref().map_or(false, |(_k, r)| {
if r.rel_of.is_none() {
r.is_local_empty()
} else {
r.has_leading_combinator()
}
})
}
pub(super) fn append(&self, other: &Self) -> Result<Self, AppendError> {
if self.is_local_empty() {
Err(AppendError::Parent)
} else if let Some(rel) = other.rel_of.clone() {
Ok(Self {
rel_of: Some(Box::new((rel.0, self.append(&rel.1)?))),
compound: other.compound.clone(),
})
} else if other.compound.cant_append() {
Err(AppendError::Sub)
} else {
Ok(Self {
rel_of: self.rel_of.clone(),
compound: self.compound.append(&other.compound)?,
})
}
}
pub(super) fn extend(
self,
extendee: &SelectorSet,
extender: &SelectorSet,
) -> Vec<Self> {
let mut result = vec![self];
for original in &extendee.s {
result = result
.into_iter()
.flat_map(|mut s| {
if original.is_superselector(&s) {
let base = s.clone();
s.compound.dedup(&original.compound);
let mut result = extender
.s
.iter()
.flat_map(|r| s.unify_extend(r))
.collect::<Vec<_>>();
result.retain(|r| !base.is_superselector(r));
result.insert(0, base);
result
} else {
vec![s]
}
})
.collect();
}
result
}
pub(super) fn nest(&self, other: &Self) -> Self {
let mut result = other.clone();
if !self.is_local_empty() || self.rel_of.is_some() {
result.rel_of = match result.rel_of.take().map(|b| *b) {
Some((kind, rel)) => {
let rel = self.nest(&rel);
if rel.is_local_empty() {
match rel.rel_of.map(|b| *b) {
Some((rk, rr)) => match (kind, rk) {
(kind, RelKind::Ancestor) => {
Some(Box::new((kind, rr)))
}
(kind, rk) => Some(Box::new((
kind,
Selector {
rel_of: Some(Box::new((rk, rr))),
..Default::default()
},
))),
},
None => None,
}
} else {
Some(Box::new((kind, rel)))
}
}
None => Some(Box::new((RelKind::Ancestor, self.clone()))),
};
}
result
}
pub(super) fn resolve_ref(mut self, ctx: &CssSelectorSet) -> Vec<Self> {
self = self.resolve_ref_in_pseudo(ctx);
let rel_of = self.rel_of.take();
let result = if self.compound.backref.is_some() {
self.compound.backref = None;
ctx.s
.s
.iter()
.flat_map(|s| {
Selector {
rel_of: s.rel_of.clone(),
compound: CompoundSelector::default(),
}
.unify(Selector {
rel_of: self.rel_of.clone(),
compound: s.compound.append(&self.compound).unwrap(),
})
})
.collect()
} else {
vec![self]
};
if let Some(rel_of) = rel_of {
let rels = rel_of.1.resolve_ref(ctx);
rels.into_iter()
.flat_map(|rel| {
result
.iter()
.map(|r| {
let mut r = r.to_owned();
let mut t = &mut r.rel_of;
loop {
if let Some(next) = t {
t = &mut next.1.rel_of;
} else {
let _ = t.insert(Box::new((
rel_of.0,
rel.clone(),
)));
break;
}
}
r
})
.collect::<Vec<_>>()
})
.collect()
} else {
result
}
}
pub(super) fn resolve_ref_in_pseudo(
mut self,
ctx: &CssSelectorSet,
) -> Self {
self.rel_of = self.rel_of.map(|mut rel| {
rel.1 = rel.1.resolve_ref_in_pseudo(ctx);
rel
});
self.compound.resolve_ref_in_pseudo(ctx);
self
}
pub(super) fn is_superselector(&self, sub: &Self) -> bool {
self.is_local_superselector(sub)
&& self.rel_of.as_deref().map_or(true, |(kind, s)| {
match kind {
RelKind::Ancestor => {
let mut t = sub.rel_of.as_deref();
while let Some((ss_kind, ss)) = t {
match ss_kind {
RelKind::Ancestor | RelKind::Parent => {
if s.is_superselector(ss) {
return true;
}
}
RelKind::Sibling
| RelKind::AdjacentSibling => (), }
t = ss.rel_of.as_deref();
}
false
}
RelKind::Parent => {
let t = sub.rel_of.as_deref();
if let Some((RelKind::Parent, ss)) = t {
s.is_superselector(ss)
} else {
false
}
}
RelKind::Sibling => {
let mut t = sub.rel_of.as_deref();
while let Some((ss_kind, ss)) = t {
if ss_kind != &RelKind::Sibling
&& ss_kind != &RelKind::AdjacentSibling
{
return false;
}
if s.is_superselector(ss) {
return true;
}
t = ss.rel_of.as_deref();
}
false
}
RelKind::AdjacentSibling => {
if let Some((RelKind::AdjacentSibling, ss)) =
sub.rel_of.as_deref()
{
s.is_superselector(ss)
} else {
false
}
}
}
})
}
fn is_local_superselector(&self, sub: &Self) -> bool {
self.compound.is_superselector(&sub.compound)
}
pub(super) fn replace(
mut self,
original: &SelectorSet,
replacement: &SelectorSet,
) -> Vec<Self> {
self.compound.replace_in_pseudo(original, replacement);
let mut result = vec![self];
for original in &original.s {
result = result
.into_iter()
.flat_map(|mut s| {
if original.is_superselector(&s) {
s.compound.dedup(&original.compound);
replacement
.s
.iter()
.flat_map(|r| s.clone().unify(r.clone()))
.collect()
} else {
vec![s]
}
})
.collect();
}
result
}
pub(super) fn unify(self, other: Self) -> Vec<Self> {
self._unify(other).unwrap_or_default()
}
pub(crate) fn unify_extend(&self, other: &Self) -> Vec<Self> {
self.clone()._unify(other.clone()).unwrap_or_default()
}
fn _unify(self, other: Self) -> Option<Vec<Self>> {
let rel_of = match (self.rel_of, other.rel_of) {
(None, None) => vec![],
(None, Some(rel)) | (Some(rel), None) => vec![rel],
(Some(a), Some(b)) => {
let v = unify_relbox(a, b)?;
if v.is_empty() {
return None;
}
v
}
};
let compound = self.compound.unify(other.compound)?;
Some(if rel_of.is_empty() {
vec![compound.into()]
} else if compound.is_empty() {
vec![]
} else if rel_of.len() == 1 {
let mut rel_of = rel_of;
vec![Selector {
rel_of: rel_of.pop(),
compound,
}]
} else {
rel_of
.into_iter()
.map(|r| Selector {
rel_of: Some(r),
compound: compound.clone(),
})
.collect()
})
}
pub(super) fn is_complex(&self) -> bool {
self.rel_of.is_some()
}
fn is_local_empty(&self) -> bool {
self.compound.is_empty()
}
pub(super) fn has_backref(&self) -> bool {
self.compound.has_backref()
|| self
.rel_of
.as_deref()
.map_or(false, |(_, s)| s.has_backref())
}
fn add_root_ancestor(&mut self, ancestor: Self) {
if let Some(rel) = self.rel_of.as_mut() {
if rel.1.is_local_empty() && !rel.1.is_complex() {
rel.1 = ancestor;
} else {
rel.1.add_root_ancestor(ancestor);
}
} else {
self.rel_of = Some(Box::new((RelKind::Ancestor, ancestor)));
}
}
fn with_rel_of(mut self, rel: RelKind, other: Self) -> Vec<Self> {
if self.rel_of.is_some() {
self.unify(Self {
rel_of: Some(Box::new((rel, other))),
..Self::default()
})
} else if self.compound.is_rootish() {
vec![]
} else {
self.rel_of = Some(Box::new((rel, other)));
vec![self]
}
}
pub(crate) fn simple_selectors(&self) -> Result<Vec<String>, Invalid> {
if self.rel_of.is_some() {
return Err(Invalid::AtError(
"Combinators not allowed in simple-selectors.".into(),
));
}
Ok(self.compound.simple_selectors())
}
pub fn write_to(&self, buf: &mut CssBuf) {
if let Some((kind, sel)) = self.rel_of.as_deref() {
sel.write_to(buf);
if let Some(symbol) = kind.symbol() {
if !sel.is_local_empty() {
buf.add_one(" ", "");
}
buf.add_str(symbol);
buf.add_one(" ", "");
} else {
buf.add_str(" ");
}
}
self.compound.write_to(buf);
}
pub(super) fn into_string_vec(self) -> Vec<String> {
let mut vec = if let Some((kind, sel)) = self.rel_of.map(|b| *b) {
let mut vec = sel.into_string_vec();
if let Some(symbol) = kind.symbol() {
vec.push(symbol.to_string());
}
vec
} else {
Vec::new()
};
if !self.compound.is_empty() {
let mut buf = CssBuf::new(Default::default());
self.compound.write_to(&mut buf);
vec.push(String::from_utf8_lossy(&buf.take()).to_string());
}
vec
}
pub(super) fn _try_from_value(v: &Value) -> Result<Self, BadSelector0> {
match v {
Value::List(list, None | Some(ListSeparator::Space), _) => list
.iter()
.try_fold(None, |a, v| {
let mut s = match v {
Value::Literal(s) => ParseError::check(
parser::selector(input_span(s.value()).borrow()),
)?,
_ => return Err(BadSelector0::Value),
};
if let Some(a) = a {
s.add_root_ancestor(a);
}
Ok(Some(s))
})
.map(Option::unwrap_or_default),
Value::Literal(s) => {
if s.value().is_empty() {
Ok(Self::default())
} else {
let span = input_span(s.value());
Ok(ParseError::check(parser::selector(span.borrow()))?)
}
}
_ => Err(BadSelector0::Value),
}
}
}
impl From<CompoundSelector> for Selector {
fn from(compound: CompoundSelector) -> Self {
Self {
rel_of: None,
compound,
}
}
}
pub(super) enum AppendError {
Parent,
Sub,
Selector(BadSelector),
}
impl AppendError {
pub(super) fn context(self, e: &Selector, b: &Selector) -> CallError {
match self {
Self::Parent => {
CallError::msg(
format!(
"Selector {:?} can't be used as a parent in a compound selector.",
show(b)
)
)
},
Self::Sub => {
CallError::msg(
format!("Can't append {} to {}.", show(e), show(b))
)
},
Self::Selector(b) => b.into(),
}
}
}
fn show(s: &Selector) -> String {
s.clone().into_string_vec().join(" ")
}
impl From<ParseError> for AppendError {
fn from(value: ParseError) -> Self {
Self::Selector(value.into())
}
}
impl From<BadSelector> for AppendError {
fn from(value: BadSelector) -> Self {
Self::Selector(value)
}
}
#[allow(clippy::boxed_local)]
fn unify_relbox(a: RelBox, b: RelBox) -> Option<Vec<RelBox>> {
use RelKind::*;
fn as_rel_vec(
kind: RelKind,
s: impl IntoIterator<Item = Selector>,
) -> Vec<RelBox> {
s.into_iter().map(|s| Box::new((kind, s))).collect()
}
if a.0 == b.0 && a.1.compound.is_rootish() && b.1.compound.is_rootish() {
return Some(as_rel_vec(a.0, a.1.unify(b.1)));
}
Some(match (*a, *b) {
((k @ AdjacentSibling, a), (AdjacentSibling, b))
| ((k @ Parent, a), (Parent, b)) => as_rel_vec(k, a._unify(b)?),
((Ancestor, a), (Ancestor, b)) => {
if b.is_local_superselector(&a) {
as_rel_vec(Ancestor, a._unify(b)?)
} else if a.is_local_superselector(&b)
|| a.compound.must_not_inherit(&b.compound)
{
as_rel_vec(Ancestor, b._unify(a)?)
} else {
as_rel_vec(
Ancestor,
b.clone()
.with_rel_of(Ancestor, a.clone())
.into_iter()
.chain(a.with_rel_of(Ancestor, b)),
)
}
}
((k @ Sibling, a), (Sibling, b)) => {
if a.is_superselector(&b) {
as_rel_vec(Sibling, once(b))
} else if b.is_superselector(&a) {
as_rel_vec(Sibling, once(a))
} else if !a.compound.must_not_inherit(&b.compound) {
as_rel_vec(
Sibling,
b.clone()
.with_rel_of(k, a.clone())
.into_iter()
.chain(a.clone().with_rel_of(k, b.clone()))
.chain(a.unify(b)),
)
} else {
as_rel_vec(Sibling, a.unify(b))
}
}
((a_k @ AdjacentSibling, a_s), (Sibling, b_s))
| ((Sibling, b_s), (a_k @ AdjacentSibling, a_s)) => {
if b_s.is_superselector(&a_s) {
as_rel_vec(a_k, once(a_s))
} else if a_s.compound.has_id() || b_s.compound.has_id() {
as_rel_vec(a_k, a_s.with_rel_of(Sibling, b_s))
} else {
as_rel_vec(
a_k,
a_s.clone()
.with_rel_of(Sibling, b_s.clone())
.into_iter()
.chain(b_s.unify(a_s)),
)
}
}
((a_k @ (AdjacentSibling | Sibling), a_s), (b_k, b_s))
| ((b_k, b_s), (a_k @ (AdjacentSibling | Sibling), a_s)) => {
as_rel_vec(a_k, a_s.with_rel_of(b_k, b_s))
}
((Parent, p), (Ancestor, a)) | ((Ancestor, a), (Parent, p)) => {
if a.is_superselector(&p) {
as_rel_vec(Parent, once(p))
} else {
as_rel_vec(Parent, p.with_rel_of(RelKind::Ancestor, a))
}
}
})
}
impl TryFrom<Value> for Selector {
type Error = BadSelector;
fn try_from(value: Value) -> Result<Self, Self::Error> {
Self::_try_from_value(&value).map_err(move |e| e.ctx(value))
}
}
impl From<Selector> for Value {
fn from(value: Selector) -> Self {
Self::List(
value
.into_string_vec()
.into_iter()
.map(Self::from)
.collect(),
Some(ListSeparator::Space),
false,
)
}
}
impl fmt::Debug for Selector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("Selector");
if let Some((kind, rel)) = self.rel_of.as_deref() {
s.field(&format!("{kind:?}"), &rel);
}
s.field("compound", &NoAlt { t: &self.compound });
s.finish()
}
}
struct NoAlt<T> {
t: T,
}
impl<T: fmt::Debug> fmt::Debug for NoAlt<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.t)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum RelKind {
Ancestor,
Parent,
Sibling,
AdjacentSibling,
}
impl RelKind {
fn symbol(self) -> Option<&'static str> {
match self {
Self::Ancestor => None,
Self::Parent => Some(">"),
Self::Sibling => Some("~"),
Self::AdjacentSibling => Some("+"),
}
}
}
pub(crate) mod parser {
use super::super::parser::compound_selector;
use super::{RelKind, Selector};
use crate::parser::util::{opt_spacelike, spacelike};
use crate::parser::{PResult, Span};
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::{into, opt, value, verify};
use nom::multi::fold_many0;
use nom::sequence::{delimited, pair, preceded, terminated};
use nom::Parser as _;
pub(crate) fn selector(input: Span) -> PResult<Selector> {
let (input, prerel) =
preceded(opt_spacelike, opt(explicit_rel_kind)).parse(input)?;
let (input, first) = if let Some(prerel) = prerel {
let (input, first) = opt(compound_selector).parse(input)?;
let first = Selector {
rel_of: Some(Box::new((prerel, Selector::default()))),
compound: first.unwrap_or_default(),
};
(input, first)
} else {
into(compound_selector).parse(input)?
};
terminated(
fold_many0(
verify(pair(rel_kind, opt(compound_selector)), |(k, s)| {
k.symbol().is_some() || s.is_some()
}),
move || first.clone(),
|rel, (kind, e)| Selector {
compound: e.unwrap_or_default(),
rel_of: Some(Box::new((kind, rel))),
},
),
opt_spacelike,
)
.parse(input)
}
fn explicit_rel_kind(input: Span) -> PResult<RelKind> {
delimited(
opt_spacelike,
alt((
value(RelKind::AdjacentSibling, tag("+")),
value(RelKind::Sibling, tag("~")),
value(RelKind::Parent, tag(">")),
)),
opt_spacelike,
)
.parse(input)
}
fn rel_kind(input: Span) -> PResult<RelKind> {
alt((explicit_rel_kind, value(RelKind::Ancestor, spacelike)))
.parse(input)
}
}