use super::super::util::{ignore_comments, opt_spacelike, spacelike2};
use super::super::{input_to_string, PResult, Span};
use super::strings::{css_string, css_string_any};
use crate::css::{Selector, SelectorPart, Selectors};
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::one_of;
use nom::combinator::{into, map, map_res, opt, value};
use nom::multi::{many1, separated_list1};
use nom::sequence::{delimited, pair, terminated, tuple};
pub fn selectors(input: Span) -> PResult<Selectors> {
map(
separated_list1(terminated(tag(","), ignore_comments), selector),
Selectors::new,
)(input)
}
pub fn selector(input: Span) -> PResult<Selector> {
let (input, mut s) = many1(selector_part)(input)?;
if s.last() == Some(&SelectorPart::Descendant) {
s.pop();
}
Ok((input, Selector(s)))
}
pub fn selector_part(input: Span) -> PResult<SelectorPart> {
let (input, mark) =
alt((tag("*"), tag("&"), tag("::"), tag(":"), tag("["), tag("")))(
input,
)?;
match mark.fragment() {
b"*" => value(SelectorPart::Simple("*".into()), tag(""))(input),
b"&" => value(SelectorPart::BackRef, tag(""))(input),
b"::" => map(
pair(
into(css_string),
opt(delimited(tag("("), selectors, tag(")"))),
),
|(name, arg)| SelectorPart::PseudoElement { name, arg },
)(input),
b":" => map(
pair(
into(css_string),
opt(delimited(tag("("), selectors, tag(")"))),
),
|(name, arg)| SelectorPart::Pseudo { name, arg },
)(input),
b"[" => delimited(
opt_spacelike,
alt((
map(
tuple((
terminated(css_string, opt_spacelike),
terminated(
map_res(
alt((
tag("*="),
tag("|="),
tag("="),
tag("$="),
tag("~="),
tag("^="),
)),
input_to_string,
),
opt_spacelike,
),
terminated(css_string_any, opt_spacelike),
opt(terminated(
one_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz",
),
opt_spacelike,
)),
)),
|(name, op, val, modifier)| SelectorPart::Attribute {
name: name.into(),
op,
val,
modifier,
},
),
map(terminated(css_string, opt_spacelike), |name| {
SelectorPart::Attribute {
name: name.into(),
op: "".to_string(),
val: "".into(),
modifier: None,
}
}),
)),
tag("]"),
)(input),
b"" => alt((
map(css_string, SelectorPart::Simple),
delimited(
opt_spacelike,
alt((
value(SelectorPart::RelOp(b'>'), tag(">")),
value(SelectorPart::RelOp(b'+'), tag("+")),
value(SelectorPart::RelOp(b'~'), tag("~")),
value(SelectorPart::RelOp(b'\\'), tag("\\")),
)),
opt_spacelike,
),
value(SelectorPart::Descendant, spacelike2),
))(input),
_ => unreachable!(),
}
}