Skip to content

Tweak at-rule parsing API to allow a single at-rule to be block or blockless. #287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cssparser"
version = "0.28.1"
version = "0.29.0"
authors = [ "Simon Sapin <simon.sapin@exyr.org>" ]

description = "Rust implementation of CSS Syntax Level 3"
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub use crate::parser::{BasicParseError, BasicParseErrorKind, ParseError, ParseE
pub use crate::parser::{Delimiter, Delimiters, Parser, ParserInput, ParserState};
pub use crate::rules_and_declarations::{parse_important, parse_one_declaration};
pub use crate::rules_and_declarations::{parse_one_rule, RuleListParser};
pub use crate::rules_and_declarations::{AtRuleParser, AtRuleType, QualifiedRuleParser};
pub use crate::rules_and_declarations::{AtRuleParser, QualifiedRuleParser};
pub use crate::rules_and_declarations::{DeclarationListParser, DeclarationParser};
pub use crate::serializer::{serialize_identifier, serialize_name, serialize_string};
pub use crate::serializer::{CssStringWriter, ToCss, TokenSerializationType};
Expand Down
70 changes: 18 additions & 52 deletions src/rules_and_declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,6 @@ pub fn parse_important<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), BasicPa
input.expect_ident_matching("important")
}

/// The return value for `AtRuleParser::parse_prelude`.
/// Indicates whether the at-rule is expected to have a `{ /* ... */ }` block
/// or end with a `;` semicolon.
pub enum AtRuleType<P, PB> {
/// The at-rule is expected to end with a `;` semicolon. Example: `@import`.
///
/// The value is the representation of all data of the rule which would be
/// handled in rule_without_block.
WithoutBlock(P),

/// The at-rule is expected to have a a `{ /* ... */ }` block. Example: `@media`
///
/// The value is the representation of the "prelude" part of the rule.
WithBlock(PB),
}

/// A trait to provide various parsing of declaration values.
///
/// For example, there could be different implementations for property declarations in style rules
Expand Down Expand Up @@ -79,11 +63,8 @@ pub trait DeclarationParser<'i> {
/// so that `impl AtRuleParser<(), ()> for ... {}` can be used
/// for using `DeclarationListParser` to parse a declarations list with only qualified rules.
pub trait AtRuleParser<'i> {
/// The intermediate representation of prelude of an at-rule without block;
type PreludeNoBlock;

/// The intermediate representation of prelude of an at-rule with block;
type PreludeBlock;
/// The intermediate representation of prelude of an at-rule.
type Prelude;

/// The finished representation of an at-rule.
type AtRule;
Expand All @@ -96,8 +77,6 @@ pub trait AtRuleParser<'i> {
/// Return the representation of the prelude and the type of at-rule,
/// or `Err(())` to ignore the entire at-rule as invalid.
///
/// See `AtRuleType`’s documentation for the return value.
///
/// The prelude is the part after the at-keyword
/// and before the `;` semicolon or `{ /* ... */ }` block.
///
Expand All @@ -112,8 +91,7 @@ pub trait AtRuleParser<'i> {
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<AtRuleType<Self::PreludeNoBlock, Self::PreludeBlock>, ParseError<'i, Self::Error>>
{
) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
let _ = name;
let _ = input;
Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name)))
Expand All @@ -129,15 +107,12 @@ pub trait AtRuleParser<'i> {
/// the end of the input.
fn rule_without_block(
&mut self,
prelude: Self::PreludeNoBlock,
prelude: Self::Prelude,
start: &ParserState,
) -> Self::AtRule {
) -> Result<Self::AtRule, ()> {
let _ = prelude;
let _ = start;
panic!(
"The `AtRuleParser::rule_without_block` method must be overriden \
if `AtRuleParser::parse_prelude` ever returns `AtRuleType::WithoutBlock`."
)
Err(())
}

/// Parse the content of a `{ /* ... */ }` block for the body of the at-rule.
Expand All @@ -152,7 +127,7 @@ pub trait AtRuleParser<'i> {
/// was indeed found following the prelude.
fn parse_block<'t>(
&mut self,
prelude: Self::PreludeBlock,
prelude: Self::Prelude,
start: &ParserState,
input: &mut Parser<'i, 't>,
) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
Expand Down Expand Up @@ -468,39 +443,30 @@ where
let callback = |input: &mut Parser<'i, '_>| parser.parse_prelude(name, input);
let result = parse_until_before(input, delimiters, callback);
match result {
Ok(AtRuleType::WithoutBlock(prelude)) => match input.next() {
Ok(&Token::Semicolon) | Err(_) => Ok(parser.rule_without_block(prelude, start)),
Ok(&Token::CurlyBracketBlock) => Err((
input.new_unexpected_token_error(Token::CurlyBracketBlock),
input.slice_from(start.position()),
)),
Ok(_) => unreachable!(),
},
Ok(AtRuleType::WithBlock(prelude)) => {
match input.next() {
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) => {
// FIXME: https://github.com/servo/rust-cssparser/issues/254
let callback =
|input: &mut Parser<'i, '_>| parser.parse_block(prelude, start, input);
parse_nested_block(input, callback)
.map_err(|e| (e, input.slice_from(start.position())))
}
Ok(&Token::Semicolon) => Err((
input.new_unexpected_token_error(Token::Semicolon),
input.slice_from(start.position()),
)),
Err(e) => Err((e.into(), input.slice_from(start.position()))),
},
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)))
}
},
}
}

Expand Down
14 changes: 6 additions & 8 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use self::test::Bencher;

use super::{
parse_important, parse_nth, parse_one_declaration, parse_one_rule, stylesheet_encoding,
AtRuleParser, AtRuleType, BasicParseError, BasicParseErrorKind, Color, CowRcStr,
AtRuleParser, BasicParseError, BasicParseErrorKind, Color, CowRcStr,
DeclarationListParser, DeclarationParser, Delimiter, EncodingSupport, ParseError,
ParseErrorKind, Parser, ParserInput, ParserState, QualifiedRuleParser, RuleListParser,
SourceLocation, ToCss, Token, TokenSerializationType, UnicodeRange, RGBA,
Expand Down Expand Up @@ -922,33 +922,31 @@ impl<'i> DeclarationParser<'i> for JsonParser {
}

impl<'i> AtRuleParser<'i> for JsonParser {
type PreludeNoBlock = Vec<Value>;
type PreludeBlock = Vec<Value>;
type Prelude = Vec<Value>;
type AtRule = Value;
type Error = ();

fn parse_prelude<'t>(
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<AtRuleType<Vec<Value>, Vec<Value>>, ParseError<'i, ()>> {
) -> Result<Vec<Value>, ParseError<'i, ()>> {
let prelude = vec![
"at-rule".to_json(),
name.to_json(),
Value::Array(component_values_to_json(input)),
];
match_ignore_ascii_case! { &*name,
"media" | "foo-with-block" => Ok(AtRuleType::WithBlock(prelude)),
"charset" => {
Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone()).into()))
},
_ => Ok(AtRuleType::WithoutBlock(prelude)),
_ => Ok(prelude),
}
}

fn rule_without_block(&mut self, mut prelude: Vec<Value>, _: &ParserState) -> Value {
fn rule_without_block(&mut self, mut prelude: Vec<Value>, _: &ParserState) -> Result<Value, ()> {
prelude.push(Value::Null);
Value::Array(prelude)
Ok(Value::Array(prelude))
}

fn parse_block<'t>(
Expand Down