use super::{BasicParseError, BasicParseErrorKind, Delimiter, ParseError, Parser, Token};
use crate::cow_rc_str::CowRcStr;
use crate::parser::{parse_nested_block, parse_until_after, ParseUntilErrorBehavior, ParserState};
pub fn parse_important<'i>(input: &mut Parser<'i, '_>) -> Result<(), BasicParseError<'i>> {
input.expect_delim('!')?;
input.expect_ident_matching("important")
}
pub trait DeclarationParser<'i> {
type Declaration;
type Error: 'i;
fn parse_value<'t>(
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
_declaration_start: &ParserState,
) -> Result<Self::Declaration, ParseError<'i, Self::Error>> {
Err(input.new_error(BasicParseErrorKind::UnexpectedToken(Token::Ident(name))))
}
}
pub trait AtRuleParser<'i> {
type Prelude;
type AtRule;
type Error: 'i;
fn parse_prelude<'t>(
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name)))
}
#[allow(clippy::result_unit_err)]
fn rule_without_block(
&mut self,
prelude: Self::Prelude,
start: &ParserState,
) -> Result<Self::AtRule, ()> {
let _ = prelude;
let _ = start;
Err(())
}
fn parse_block<'t>(
&mut self,
prelude: Self::Prelude,
start: &ParserState,
input: &mut Parser<'i, 't>,
) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
let _ = prelude;
let _ = start;
Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid))
}
}
pub trait QualifiedRuleParser<'i> {
type Prelude;
type QualifiedRule;
type Error: 'i;
fn parse_prelude<'t>(
&mut self,
input: &mut Parser<'i, 't>,
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
}
fn parse_block<'t>(
&mut self,
prelude: Self::Prelude,
start: &ParserState,
input: &mut Parser<'i, 't>,
) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
let _ = prelude;
let _ = start;
Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
}
}
pub struct RuleBodyParser<'i, 't, 'a, P, I, E> {
pub input: &'a mut Parser<'i, 't>,
pub parser: &'a mut P,
_phantom: std::marker::PhantomData<(I, E)>,
}
pub trait RuleBodyItemParser<'i, DeclOrRule, Error: 'i>:
DeclarationParser<'i, Declaration = DeclOrRule, Error = Error>
+ QualifiedRuleParser<'i, QualifiedRule = DeclOrRule, Error = Error>
+ AtRuleParser<'i, AtRule = DeclOrRule, Error = Error>
{
fn parse_declarations(&self) -> bool;
fn parse_qualified(&self) -> bool;
}
impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> {
pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self {
Self {
input,
parser,
_phantom: std::marker::PhantomData,
}
}
}
impl<'i, I, P, E: 'i> Iterator for RuleBodyParser<'i, '_, '_, P, I, E>
where
P: RuleBodyItemParser<'i, I, E>,
{
type Item = Result<I, (ParseError<'i, E>, &'i str)>;
fn next(&mut self) -> Option<Self::Item> {
loop {
self.input.skip_whitespace();
let start = self.input.state();
match self.input.next_including_whitespace_and_comments().ok()? {
Token::CloseCurlyBracket
| Token::WhiteSpace(..)
| Token::Semicolon
| Token::Comment(..) => continue,
Token::AtKeyword(ref name) => {
let name = name.clone();
return Some(parse_at_rule(&start, name, self.input, &mut *self.parser));
}
Token::Ident(ref name) if self.parser.parse_declarations() => {
let name = name.clone();
let parse_qualified = self.parser.parse_qualified();
let result = {
let error_behavior = if parse_qualified {
ParseUntilErrorBehavior::Stop
} else {
ParseUntilErrorBehavior::Consume
};
let parser = &mut self.parser;
parse_until_after(
self.input,
Delimiter::Semicolon,
error_behavior,
|input| {
input.expect_colon()?;
parser.parse_value(name, input, &start)
},
)
};
if result.is_err() && parse_qualified {
self.input.reset(&start);
if let Ok(qual) = parse_qualified_rule(
&start,
self.input,
&mut *self.parser,
true,
) {
return Some(Ok(qual));
}
}
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
}
token => {
let result = if self.parser.parse_qualified() {
self.input.reset(&start);
let nested = self.parser.parse_declarations();
parse_qualified_rule(&start, self.input, &mut *self.parser, nested)
} else {
let token = token.clone();
self.input.parse_until_after(Delimiter::Semicolon, |_| {
Err(start.source_location().new_unexpected_token_error(token))
})
};
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
}
}
}
}
}
pub struct StyleSheetParser<'i, 't, 'a, P> {
pub input: &'a mut Parser<'i, 't>,
pub parser: &'a mut P,
any_rule_so_far: bool,
}
impl<'i, 't, 'a, R, P, E: 'i> StyleSheetParser<'i, 't, 'a, P>
where
P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E>
+ AtRuleParser<'i, AtRule = R, Error = E>,
{
pub fn new(input: &'a mut Parser<'i, 't>, parser: &'a mut P) -> Self {
Self {
input,
parser,
any_rule_so_far: false,
}
}
}
impl<'i, R, P, E: 'i> Iterator for StyleSheetParser<'i, '_, '_, P>
where
P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E>
+ AtRuleParser<'i, AtRule = R, Error = E>,
{
type Item = Result<R, (ParseError<'i, E>, &'i str)>;
fn next(&mut self) -> Option<Self::Item> {
loop {
self.input.skip_cdc_and_cdo();
let start = self.input.state();
let at_keyword = match self.input.next_byte()? {
b'@' => match self.input.next_including_whitespace_and_comments() {
Ok(Token::AtKeyword(name)) => Some(name.clone()),
_ => {
self.input.reset(&start);
None
}
},
_ => None,
};
if let Some(name) = at_keyword {
let first_stylesheet_rule = !self.any_rule_so_far;
self.any_rule_so_far = true;
if first_stylesheet_rule && name.eq_ignore_ascii_case("charset") {
let delimiters = Delimiter::Semicolon | Delimiter::CurlyBracketBlock;
let _: Result<(), ParseError<()>> =
self.input.parse_until_after(delimiters, |_| Ok(()));
} else {
return Some(parse_at_rule(
&start,
name.clone(),
self.input,
&mut *self.parser,
));
}
} else {
self.any_rule_so_far = true;
let result = parse_qualified_rule(
&start,
self.input,
&mut *self.parser,
false,
);
return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))));
}
}
}
}
pub fn parse_one_declaration<'i, 't, P, E>(
input: &mut Parser<'i, 't>,
parser: &mut P,
) -> Result<<P as DeclarationParser<'i>>::Declaration, (ParseError<'i, E>, &'i str)>
where
P: DeclarationParser<'i, Error = E>,
{
let start = input.state();
let start_position = input.position();
input
.parse_entirely(|input| {
let name = input.expect_ident()?.clone();
input.expect_colon()?;
parser.parse_value(name, input, &start)
})
.map_err(|e| (e, input.slice_from(start_position)))
}
pub fn parse_one_rule<'i, 't, R, P, E>(
input: &mut Parser<'i, 't>,
parser: &mut P,
) -> Result<R, ParseError<'i, E>>
where
P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E>
+ AtRuleParser<'i, AtRule = R, Error = E>,
{
input.parse_entirely(|input| {
input.skip_whitespace();
let start = input.state();
let at_keyword = if input.next_byte() == Some(b'@') {
match *input.next_including_whitespace_and_comments()? {
Token::AtKeyword(ref name) => Some(name.clone()),
_ => {
input.reset(&start);
None
}
}
} else {
None
};
if let Some(name) = at_keyword {
parse_at_rule(&start, name, input, parser).map_err(|e| e.0)
} else {
parse_qualified_rule(&start, input, parser, false)
}
})
}
fn parse_at_rule<'i, 't, P, E>(
start: &ParserState,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
parser: &mut P,
) -> Result<<P as AtRuleParser<'i>>::AtRule, (ParseError<'i, E>, &'i str)>
where
P: AtRuleParser<'i, Error = E>,
{
let delimiters = Delimiter::Semicolon | Delimiter::CurlyBracketBlock;
let result = input.parse_until_before(delimiters, |input| parser.parse_prelude(name, input));
match result {
Ok(prelude) => {
let result = match input.next() {
Ok(&Token::Semicolon) | Err(_) => parser
.rule_without_block(prelude, start)
.map_err(|()| input.new_unexpected_token_error(Token::Semicolon)),
Ok(&Token::CurlyBracketBlock) => {
parse_nested_block(input, |input| parser.parse_block(prelude, start, input))
}
Ok(_) => unreachable!(),
};
result.map_err(|e| (e, input.slice_from(start.position())))
}
Err(error) => {
let end_position = input.position();
match input.next() {
Ok(&Token::CurlyBracketBlock) | Ok(&Token::Semicolon) | Err(_) => {}
_ => unreachable!(),
};
Err((error, input.slice(start.position()..end_position)))
}
}
}
fn looks_like_a_custom_property(input: &mut Parser) -> bool {
let ident = match input.expect_ident() {
Ok(i) => i,
Err(..) => return false,
};
ident.starts_with("--") && input.expect_colon().is_ok()
}
fn parse_qualified_rule<'i, 't, P, E>(
start: &ParserState,
input: &mut Parser<'i, 't>,
parser: &mut P,
nested: bool,
) -> Result<<P as QualifiedRuleParser<'i>>::QualifiedRule, ParseError<'i, E>>
where
P: QualifiedRuleParser<'i, Error = E>,
{
input.skip_whitespace();
let prelude = {
let state = input.state();
if looks_like_a_custom_property(input) {
let delimiters = if nested {
Delimiter::Semicolon
} else {
Delimiter::CurlyBracketBlock
};
let _: Result<(), ParseError<()>> = input.parse_until_after(delimiters, |_| Ok(()));
return Err(state
.source_location()
.new_error(BasicParseErrorKind::QualifiedRuleInvalid));
}
let delimiters = if nested {
Delimiter::Semicolon | Delimiter::CurlyBracketBlock
} else {
Delimiter::CurlyBracketBlock
};
input.reset(&state);
input.parse_until_before(delimiters, |input| parser.parse_prelude(input))
};
input.expect_curly_bracket_block()?;
let prelude = prelude?;
parse_nested_block(input, |input| parser.parse_block(prelude, start, input))
}