From aaa966d9d6ae70c4b8a62bb5e3a14c068bb7dff0 Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Tue, 14 Nov 2023 07:46:37 -0700 Subject: [PATCH 01/21] Replace TokenSerializationType with TokenSerializationTypeVariants (#369) * Replace TokenSerializationType with TokenSerializationTypeVariants Making TokenSerializationType public is useful to describe a CSS string that is already known to be valid in cases when the TokenSerializationTypes of the start and end of the CSS string is needed, but Tokens are not. This helps with creating a custom_properties::VariableValue from the CSS string of a computed value for CSS Properties and Values, see Mozilla bug 1858305 [1]. [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1858305 * Derive Default trait for TokenSerializationType * Deprecate TokenSerializationType::nothing() --- src/serializer.rs | 160 ++++++++++++++++++++++++++++++++-------------- src/tests.rs | 2 +- 2 files changed, 112 insertions(+), 50 deletions(-) diff --git a/src/serializer.rs b/src/serializer.rs index 19f01456..09c22402 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -378,19 +378,110 @@ impl_tocss_for_float!(f32); impl_tocss_for_float!(f64); /// A category of token. See the `needs_separator_when_before` method. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub struct TokenSerializationType(TokenSerializationTypeVariants); +#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)] +pub enum TokenSerializationType { + /// No token serialization type. + #[default] + Nothing, + + /// The [``](https://drafts.csswg.org/css-syntax/#whitespace-token-diagram) + /// type. + WhiteSpace, + + /// The [``](https://drafts.csswg.org/css-syntax/#at-keyword-token-diagram) + /// type, the "[``](https://drafts.csswg.org/css-syntax/#hash-token-diagram) with + /// the type flag set to 'unrestricted'" type, or the + /// "[``](https://drafts.csswg.org/css-syntax/#hash-token-diagram) with the type + /// flag set to 'id'" type. + AtKeywordOrHash, + + /// The [``](https://drafts.csswg.org/css-syntax/#number-token-diagram) type. + Number, + + /// The [``](https://drafts.csswg.org/css-syntax/#dimension-token-diagram) + /// type. + Dimension, + + /// The [``](https://drafts.csswg.org/css-syntax/#percentage-token-diagram) + /// type. + Percentage, + + /// The [``](https://drafts.csswg.org/css-syntax/#url-token-diagram) or + /// `` type. + UrlOrBadUrl, + + /// The [``](https://drafts.csswg.org/css-syntax/#function-token-diagram) type. + Function, + + /// The [``](https://drafts.csswg.org/css-syntax/#ident-token-diagram) type. + Ident, + + /// The `-->` [``](https://drafts.csswg.org/css-syntax/#CDC-token-diagram) type. + CDC, + + /// The `|=` + /// [``](https://drafts.csswg.org/css-syntax/#dash-match-token-diagram) type. + DashMatch, + + /// The `*=` + /// [``](https://drafts.csswg.org/css-syntax/#substring-match-token-diagram) + /// type. + SubstringMatch, + + /// The `<(-token>` type. + OpenParen, + + /// The `#` `` type. + DelimHash, + + /// The `@` `` type. + DelimAt, + + /// The `.` or `+` `` type. + DelimDotOrPlus, + + /// The `-` `` type. + DelimMinus, + + /// The `?` `` type. + DelimQuestion, + + /// The `$`, `^`, or `~` `` type. + DelimAssorted, + + /// The `=` `` type. + DelimEquals, + + /// The `|` `` type. + DelimBar, + + /// The `/` `` type. + DelimSlash, + + /// The `*` `` type. + DelimAsterisk, + + /// The `%` `` type. + DelimPercent, + + /// A type indicating any other token. + Other, +} impl TokenSerializationType { /// Return a value that represents the absence of a token, e.g. before the start of the input. + #[deprecated( + since = "0.32.1", + note = "use TokenSerializationType::Nothing or TokenSerializationType::default() instead" + )] pub fn nothing() -> TokenSerializationType { - TokenSerializationType(TokenSerializationTypeVariants::Nothing) + Default::default() } - /// If this value is `TokenSerializationType::nothing()`, set it to the given value instead. + /// If this value is `TokenSerializationType::Nothing`, set it to the given value instead. pub fn set_if_nothing(&mut self, new_value: TokenSerializationType) { - if self.0 == TokenSerializationTypeVariants::Nothing { - self.0 = new_value.0 + if matches!(self, TokenSerializationType::Nothing) { + *self = new_value } } @@ -404,10 +495,10 @@ impl TokenSerializationType { /// See https://github.com/w3c/csswg-drafts/issues/4088 for the /// `DelimPercent` bits. pub fn needs_separator_when_before(self, other: TokenSerializationType) -> bool { - use self::TokenSerializationTypeVariants::*; - match self.0 { + use self::TokenSerializationType::*; + match self { Ident => matches!( - other.0, + other, Ident | Function | UrlOrBadUrl @@ -419,15 +510,15 @@ impl TokenSerializationType { | OpenParen ), AtKeywordOrHash | Dimension => matches!( - other.0, + other, Ident | Function | UrlOrBadUrl | DelimMinus | Number | Percentage | Dimension | CDC ), DelimHash | DelimMinus => matches!( - other.0, + other, Ident | Function | UrlOrBadUrl | DelimMinus | Number | Percentage | Dimension ), Number => matches!( - other.0, + other, Ident | Function | UrlOrBadUrl @@ -437,11 +528,11 @@ impl TokenSerializationType { | DelimPercent | Dimension ), - DelimAt => matches!(other.0, Ident | Function | UrlOrBadUrl | DelimMinus), - DelimDotOrPlus => matches!(other.0, Number | Percentage | Dimension), - DelimAssorted | DelimAsterisk => matches!(other.0, DelimEquals), - DelimBar => matches!(other.0, DelimEquals | DelimBar | DashMatch), - DelimSlash => matches!(other.0, DelimAsterisk | SubstringMatch), + DelimAt => matches!(other, Ident | Function | UrlOrBadUrl | DelimMinus), + DelimDotOrPlus => matches!(other, Number | Percentage | Dimension), + DelimAssorted | DelimAsterisk => matches!(other, DelimEquals), + DelimBar => matches!(other, DelimEquals | DelimBar | DashMatch), + DelimSlash => matches!(other, DelimAsterisk | SubstringMatch), Nothing | WhiteSpace | Percentage | UrlOrBadUrl | Function | CDC | OpenParen | DashMatch | SubstringMatch | DelimQuestion | DelimEquals | DelimPercent | Other => { false @@ -450,43 +541,14 @@ impl TokenSerializationType { } } -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -enum TokenSerializationTypeVariants { - Nothing, - WhiteSpace, - AtKeywordOrHash, - Number, - Dimension, - Percentage, - UrlOrBadUrl, - Function, - Ident, - CDC, - DashMatch, - SubstringMatch, - OpenParen, // '(' - DelimHash, // '#' - DelimAt, // '@' - DelimDotOrPlus, // '.', '+' - DelimMinus, // '-' - DelimQuestion, // '?' - DelimAssorted, // '$', '^', '~' - DelimEquals, // '=' - DelimBar, // '|' - DelimSlash, // '/' - DelimAsterisk, // '*' - DelimPercent, // '%' - Other, // anything else -} - impl<'a> Token<'a> { /// Categorize a token into a type that determines when `/**/` needs to be inserted /// between two tokens when serialized next to each other without whitespace in between. /// /// See the `TokenSerializationType::needs_separator_when_before` method. pub fn serialization_type(&self) -> TokenSerializationType { - use self::TokenSerializationTypeVariants::*; - TokenSerializationType(match *self { + use self::TokenSerializationType::*; + match self { Token::Ident(_) => Ident, Token::AtKeyword(_) | Token::Hash(_) | Token::IDHash(_) => AtKeywordOrHash, Token::UnquotedUrl(_) | Token::BadUrl(_) => UrlOrBadUrl, @@ -526,6 +588,6 @@ impl<'a> Token<'a> { | Token::IncludeMatch | Token::PrefixMatch | Token::SuffixMatch => Other, - }) + } } } diff --git a/src/tests.rs b/src/tests.rs index d4bc5f51..f9dea193 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -466,7 +466,7 @@ fn serializer(preserve_comments: bool) { } let mut serialized = String::new(); write_to( - TokenSerializationType::nothing(), + TokenSerializationType::Nothing, input, &mut serialized, preserve_comments, From 87438e19840e47d21f9d5b958676c333e2e46d66 Mon Sep 17 00:00:00 2001 From: Gabriel de Perthuis Date: Fri, 19 Jan 2024 18:10:25 +0100 Subject: [PATCH 02/21] Fix dependencies minimal versions (#365) Tested with: cargo +nightly update -Zminimal-versions cargo check --all-targets cargo +nightly check --all-targets --all-features --- Cargo.toml | 4 ++-- color/Cargo.toml | 2 +- macros/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab2341d8..c7beb4c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ rust-version = "1.63" exclude = ["src/css-parsing-tests/**", "src/big-data-url.css"] [dev-dependencies] -serde_json = "1.0" +serde_json = "1.0.25" difference = "2.0" encoding_rs = "0.8" @@ -23,7 +23,7 @@ encoding_rs = "0.8" cssparser-macros = {path = "./macros", version = "0.6.1"} dtoa-short = "0.3" itoa = "1.0" -phf = {version = ">=0.8,<=0.11", features = ["macros"]} +phf = {version = "0.11.2", features = ["macros"]} serde = {version = "1.0", optional = true} smallvec = "1.0" diff --git a/color/Cargo.toml b/color/Cargo.toml index d65ee3ed..d1e315a8 100644 --- a/color/Cargo.toml +++ b/color/Cargo.toml @@ -15,6 +15,6 @@ path = "lib.rs" cssparser = { version = "0.33", path = ".." } [dev-dependencies] -serde_json = "1.0" +serde_json = "1.0.25" difference = "2.0" encoding_rs = "0.8" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 1c225bf2..494705f9 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -13,5 +13,5 @@ path = "lib.rs" proc-macro = true [dependencies] -quote = "1" +quote = "1.0.29" syn = {version = "2", features = ["full", "extra-traits"]} From c3314d15b281f376538cdc91bbb9ce21d5052231 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Wed, 20 Mar 2024 10:45:34 +0100 Subject: [PATCH 03/21] Rename `master` branch to `main` (#371) This is in line with the policy decided in the TSC. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0d3c0229..07bedca5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,7 +2,7 @@ name: CI on: push: - branches: [master] + branches: [main] pull_request: workflow_dispatch: merge_group: From 08e4d5e4b3a9311b563567e151baa994ab025f75 Mon Sep 17 00:00:00 2001 From: Chipnertkj <32001812+chipnertkj@users.noreply.github.com> Date: Thu, 7 Dec 2023 02:26:33 +0100 Subject: [PATCH 04/21] Apply clippy lints for more idiomatic code --- color/tests.rs | 47 ++++++++++----------- macros/lib.rs | 20 ++++----- src/cow_rc_str.rs | 6 +-- src/macros.rs | 4 +- src/nth.rs | 16 +++---- src/parser.rs | 19 ++++----- src/rules_and_declarations.rs | 16 +++---- src/serializer.rs | 32 +++++++------- src/tests.rs | 64 +++++++++++++--------------- src/tokenizer.rs | 78 ++++++++++++++++++----------------- src/unicode_range.rs | 20 ++++----- 11 files changed, 157 insertions(+), 165 deletions(-) diff --git a/color/tests.rs b/color/tests.rs index babb076b..85eb430e 100644 --- a/color/tests.rs +++ b/color/tests.rs @@ -3,25 +3,21 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::*; -use crate::{ColorParser, PredefinedColorSpace, Color, RgbaLegacy}; -use cssparser::{Parser, ParserInput}; -use serde_json::{self, json, Value}; +use cssparser::ParserInput; +use serde_json::{json, Value}; fn almost_equals(a: &Value, b: &Value) -> bool { match (a, b) { - (&Value::Number(ref a), &Value::Number(ref b)) => { + (Value::Number(a), Value::Number(b)) => { let a = a.as_f64().unwrap(); let b = b.as_f64().unwrap(); (a - b).abs() <= a.abs() * 1e-6 } (&Value::Bool(a), &Value::Bool(b)) => a == b, - (&Value::String(ref a), &Value::String(ref b)) => a == b, - (&Value::Array(ref a), &Value::Array(ref b)) => { - a.len() == b.len() - && a.iter() - .zip(b.iter()) - .all(|(ref a, ref b)| almost_equals(*a, *b)) + (Value::String(a), Value::String(b)) => a == b, + (Value::Array(a), Value::Array(b)) => { + a.len() == b.len() && a.iter().zip(b.iter()).all(|(a, b)| almost_equals(a, b)) } (&Value::Object(_), &Value::Object(_)) => panic!("Not implemented"), (&Value::Null, &Value::Null) => true, @@ -43,8 +39,7 @@ fn assert_json_eq(results: Value, expected: Value, message: &str) { } } - -fn run_raw_json_tests ()>(json_data: &str, run: F) { +fn run_raw_json_tests(json_data: &str, run: F) { let items = match serde_json::from_str(json_data) { Ok(Value::Array(items)) => items, other => panic!("Invalid JSON: {:?}", other), @@ -92,11 +87,14 @@ fn color3() { #[cfg_attr(all(miri, feature = "skip_long_tests"), ignore)] #[test] fn color3_hsl() { - run_color_tests(include_str!("../src/css-parsing-tests/color3_hsl.json"), |c| { - c.ok() - .map(|v| v.to_css_string().to_json()) - .unwrap_or(Value::Null) - }) + run_color_tests( + include_str!("../src/css-parsing-tests/color3_hsl.json"), + |c| { + c.ok() + .map(|v| v.to_css_string().to_json()) + .unwrap_or(Value::Null) + }, + ) } /// color3_keywords.json is different: R, G and B are in 0..255 rather than 0..1 @@ -115,11 +113,14 @@ fn color3_keywords() { #[cfg_attr(all(miri, feature = "skip_long_tests"), ignore)] #[test] fn color4_hwb() { - run_color_tests(include_str!("../src/css-parsing-tests/color4_hwb.json"), |c| { - c.ok() - .map(|v| v.to_css_string().to_json()) - .unwrap_or(Value::Null) - }) + run_color_tests( + include_str!("../src/css-parsing-tests/color4_hwb.json"), + |c| { + c.ok() + .map(|v| v.to_css_string().to_json()) + .unwrap_or(Value::Null) + }, + ) } #[cfg_attr(all(miri, feature = "skip_long_tests"), ignore)] @@ -355,7 +356,7 @@ fn generic_parser() { ]; for (input, expected) in TESTS { - let mut input = ParserInput::new(*input); + let mut input = ParserInput::new(input); let mut input = Parser::new(&mut input); let actual: OutputType = parse_color_with(&TestColorParser, &mut input).unwrap(); diff --git a/macros/lib.rs b/macros/lib.rs index 4354cc8e..8b40bd4c 100644 --- a/macros/lib.rs +++ b/macros/lib.rs @@ -48,9 +48,7 @@ fn get_byte_from_lit(lit: &syn::Lit) -> u8 { fn get_byte_from_expr_lit(expr: &syn::Expr) -> u8 { match *expr { - syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => { - get_byte_from_lit(lit) - } + syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => get_byte_from_lit(lit), _ => unreachable!(), } } @@ -63,15 +61,17 @@ fn parse_pat_to_table<'a>( table: &mut [u8; 256], ) { match pat { - &syn::Pat::Lit(syn::PatLit { ref lit, .. }) => { + syn::Pat::Lit(syn::PatLit { ref lit, .. }) => { let value = get_byte_from_lit(lit); if table[value as usize] == 0 { table[value as usize] = case_id; } } - &syn::Pat::Range(syn::PatRange { ref start, ref end, .. }) => { - let lo = get_byte_from_expr_lit(&start.as_ref().unwrap()); - let hi = get_byte_from_expr_lit(&end.as_ref().unwrap()); + syn::Pat::Range(syn::PatRange { + ref start, ref end, .. + }) => { + let lo = get_byte_from_expr_lit(start.as_ref().unwrap()); + let hi = get_byte_from_expr_lit(end.as_ref().unwrap()); for value in lo..hi { if table[value as usize] == 0 { table[value as usize] = case_id; @@ -81,14 +81,14 @@ fn parse_pat_to_table<'a>( table[hi as usize] = case_id; } } - &syn::Pat::Wild(_) => { + syn::Pat::Wild(_) => { for byte in table.iter_mut() { if *byte == 0 { *byte = case_id; } } } - &syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => { + syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => { assert_eq!(*wildcard, None); *wildcard = Some(ident); for byte in table.iter_mut() { @@ -97,7 +97,7 @@ fn parse_pat_to_table<'a>( } } } - &syn::Pat::Or(syn::PatOr { ref cases, .. }) => { + syn::Pat::Or(syn::PatOr { ref cases, .. }) => { for case in cases { parse_pat_to_table(case, case_id, wildcard, table); } diff --git a/src/cow_rc_str.rs b/src/cow_rc_str.rs index ecf14a0a..26508481 100644 --- a/src/cow_rc_str.rs +++ b/src/cow_rc_str.rs @@ -4,7 +4,7 @@ use std::borrow::{Borrow, Cow}; use std::rc::Rc; -use std::{cmp, fmt, hash, marker, mem, ops, slice, str, ptr}; +use std::{cmp, fmt, hash, marker, mem, ops, ptr, slice, str}; /// A string that is either shared (heap-allocated and reference-counted) or borrowed. /// @@ -23,9 +23,9 @@ pub struct CowRcStr<'a> { phantom: marker::PhantomData>>, } -fn _static_assert_same_size<'a>() { +fn _static_assert_same_size() { // "Instantiate" the generic function without calling it. - let _ = mem::transmute::, Option>>; + let _ = mem::transmute::, Option>>; } impl<'a> From> for CowRcStr<'a> { diff --git a/src/macros.rs b/src/macros.rs index fc4b77a1..67d83658 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -182,7 +182,7 @@ pub fn _cssparser_internal_to_lowercase<'a>( let input_bytes = unsafe { &*(input.as_bytes() as *const [u8] as *const [MaybeUninit]) }; - buffer.copy_from_slice(&*input_bytes); + buffer.copy_from_slice(input_bytes); // Same as above re layout, plus these bytes have been initialized: let buffer = unsafe { &mut *(buffer as *mut [MaybeUninit] as *mut [u8]) }; @@ -195,7 +195,7 @@ pub fn _cssparser_internal_to_lowercase<'a>( } Some( - match input.bytes().position(|byte| matches!(byte, b'A'..=b'Z')) { + match input.bytes().position(|byte| byte.is_ascii_uppercase()) { Some(first_uppercase) => make_ascii_lowercase(buffer, input, first_uppercase), // common case: input is already lower-case None => input, diff --git a/src/nth.rs b/src/nth.rs index 518de4d9..e4bde0de 100644 --- a/src/nth.rs +++ b/src/nth.rs @@ -8,7 +8,7 @@ use super::{BasicParseError, Parser, ParserInput, Token}; /// The input is typically the arguments of a function, /// in which case the caller needs to check if the arguments’ parser is exhausted. /// Return `Ok((A, B))`, or `Err(())` for a syntax error. -pub fn parse_nth<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(i32, i32), BasicParseError<'i>> { +pub fn parse_nth<'i>(input: &mut Parser<'i, '_>) -> Result<(i32, i32), BasicParseError<'i>> { match *input.next()? { Token::Number { int_value: Some(b), .. @@ -22,7 +22,7 @@ pub fn parse_nth<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(i32, i32), Basic unit, "n" => Ok(parse_b(input, a)?), "n-" => Ok(parse_signless_b(input, a, -1)?), - _ => match parse_n_dash_digits(&*unit) { + _ => match parse_n_dash_digits(unit) { Ok(b) => Ok((a, b)), Err(()) => { let unit = unit.clone(); @@ -40,8 +40,8 @@ pub fn parse_nth<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(i32, i32), Basic "n-" => Ok(parse_signless_b(input, 1, -1)?), "-n-" => Ok(parse_signless_b(input, -1, -1)?), _ => { - let (slice, a) = if value.starts_with("-") { - (&value[1..], -1) + let (slice, a) = if let Some(stripped) = value.strip_prefix('-') { + (stripped, -1) } else { (&**value, 1) }; @@ -81,7 +81,7 @@ pub fn parse_nth<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(i32, i32), Basic } } -fn parse_b<'i, 't>(input: &mut Parser<'i, 't>, a: i32) -> Result<(i32, i32), BasicParseError<'i>> { +fn parse_b<'i>(input: &mut Parser<'i, '_>, a: i32) -> Result<(i32, i32), BasicParseError<'i>> { let start = input.state(); match input.next() { Ok(&Token::Delim('+')) => parse_signless_b(input, a, 1), @@ -98,8 +98,8 @@ fn parse_b<'i, 't>(input: &mut Parser<'i, 't>, a: i32) -> Result<(i32, i32), Bas } } -fn parse_signless_b<'i, 't>( - input: &mut Parser<'i, 't>, +fn parse_signless_b<'i>( + input: &mut Parser<'i, '_>, a: i32, b_sign: i32, ) -> Result<(i32, i32), BasicParseError<'i>> { @@ -118,7 +118,7 @@ fn parse_n_dash_digits(string: &str) -> Result { let bytes = string.as_bytes(); if bytes.len() >= 3 && bytes[..2].eq_ignore_ascii_case(b"n-") - && bytes[2..].iter().all(|&c| matches!(c, b'0'..=b'9')) + && bytes[2..].iter().all(|&c| c.is_ascii_digit()) { Ok(parse_number_saturate(&string[1..]).unwrap()) // Include the minus sign } else { diff --git a/src/parser.rs b/src/parser.rs index dd7777a2..424306e1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -116,7 +116,7 @@ impl<'i, T> From> for ParseError<'i, T> { impl SourceLocation { /// Create a new BasicParseError at this location for an unexpected token #[inline] - pub fn new_basic_unexpected_token_error<'i>(self, token: Token<'i>) -> BasicParseError<'i> { + pub fn new_basic_unexpected_token_error(self, token: Token<'_>) -> BasicParseError<'_> { BasicParseError { kind: BasicParseErrorKind::UnexpectedToken(token), location: self, @@ -125,7 +125,7 @@ impl SourceLocation { /// Create a new ParseError at this location for an unexpected token #[inline] - pub fn new_unexpected_token_error<'i, E>(self, token: Token<'i>) -> ParseError<'i, E> { + pub fn new_unexpected_token_error(self, token: Token<'_>) -> ParseError<'_, E> { ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(token)), location: self, @@ -652,9 +652,8 @@ impl<'i: 't, 't> Parser<'i, 't> { let token = if using_cached_token { let cached_token = self.input.cached_token.as_ref().unwrap(); self.input.tokenizer.reset(&cached_token.end_state); - match cached_token.token { - Token::Function(ref name) => self.input.tokenizer.see_function(name), - _ => {} + if let Token::Function(ref name) = cached_token.token { + self.input.tokenizer.see_function(name) } &cached_token.token } else { @@ -748,7 +747,7 @@ impl<'i: 't, 't> Parser<'i, 't> { match self.parse_until_before(Delimiter::Comma, &mut parse_one) { Ok(v) => values.push(v), Err(e) if !ignore_errors => return Err(e), - Err(_) => {}, + Err(_) => {} } match self.next() { Err(_) => return Ok(values), @@ -835,7 +834,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// expect_ident, but clone the CowRcStr #[inline] pub fn expect_ident_cloned(&mut self) -> Result, BasicParseError<'i>> { - self.expect_ident().map(|s| s.clone()) + self.expect_ident().cloned() } /// Parse a whose unescaped value is an ASCII-insensitive match for the given value. @@ -860,7 +859,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// expect_string, but clone the CowRcStr #[inline] pub fn expect_string_cloned(&mut self) -> Result, BasicParseError<'i>> { - self.expect_string().map(|s| s.clone()) + self.expect_string().cloned() } /// Parse either a or a , and return the unescaped value. @@ -879,7 +878,7 @@ impl<'i: 't, 't> Parser<'i, 't> { Token::UnquotedUrl(ref value) => Ok(value.clone()), Token::Function(ref name) if name.eq_ignore_ascii_case("url") => { self.parse_nested_block(|input| { - input.expect_string().map_err(Into::into).map(|s| s.clone()) + input.expect_string().map_err(Into::into).cloned() }) .map_err(ParseError::<()>::basic) } @@ -894,7 +893,7 @@ impl<'i: 't, 't> Parser<'i, 't> { Token::QuotedString(ref value) => Ok(value.clone()), Token::Function(ref name) if name.eq_ignore_ascii_case("url") => { self.parse_nested_block(|input| { - input.expect_string().map_err(Into::into).map(|s| s.clone()) + input.expect_string().map_err(Into::into).cloned() }) .map_err(ParseError::<()>::basic) } diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index fb33a7d0..b23e9dc0 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -14,7 +14,7 @@ use crate::parser::{parse_nested_block, parse_until_after, ParseUntilErrorBehavi /// /// Typical usage is `input.try_parse(parse_important).is_ok()` /// at the end of a `DeclarationParser::parse_value` implementation. -pub fn parse_important<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), BasicParseError<'i>> { +pub fn parse_important<'i>(input: &mut Parser<'i, '_>) -> Result<(), BasicParseError<'i>> { input.expect_delim('!')?; input.expect_ident_matching("important") } @@ -253,10 +253,10 @@ where 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::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)); @@ -294,7 +294,7 @@ where &mut *self.parser, Delimiter::Semicolon | Delimiter::CurlyBracketBlock, ) { - return Some(Ok(qual)) + return Some(Ok(qual)); } } @@ -367,7 +367,7 @@ where 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(ref name)) => Some(name.clone()), + Ok(Token::AtKeyword(name)) => Some(name.clone()), _ => { self.input.reset(&start); None @@ -503,5 +503,5 @@ where input.expect_curly_bracket_block()?; // Do this here so that we consume the `{` even if the prelude is `Err`. let prelude = prelude?; - parse_nested_block(input, |input| parser.parse_block(prelude, &start, input)) + parse_nested_block(input, |input| parser.parse_block(prelude, start, input)) } diff --git a/src/serializer.rs b/src/serializer.rs index 09c22402..403976cd 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -3,8 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::match_byte; -use dtoa_short::{self, Notation}; -use itoa; +use dtoa_short::Notation; use std::fmt::{self, Write}; use std::str; @@ -49,10 +48,9 @@ where dtoa_short::write(dest, value)? }; - if int_value.is_none() && value.fract() == 0. { - if !notation.decimal_point && !notation.scientific { - dest.write_str(".0")?; - } + if int_value.is_none() && value.fract() == 0. && !notation.decimal_point && !notation.scientific + { + dest.write_str(".0")?; } Ok(()) } @@ -63,10 +61,10 @@ impl<'a> ToCss for Token<'a> { W: fmt::Write, { match *self { - Token::Ident(ref value) => serialize_identifier(&**value, dest)?, + Token::Ident(ref value) => serialize_identifier(value, dest)?, Token::AtKeyword(ref value) => { dest.write_str("@")?; - serialize_identifier(&**value, dest)?; + serialize_identifier(value, dest)?; } Token::Hash(ref value) => { dest.write_str("#")?; @@ -74,12 +72,12 @@ impl<'a> ToCss for Token<'a> { } Token::IDHash(ref value) => { dest.write_str("#")?; - serialize_identifier(&**value, dest)?; + serialize_identifier(value, dest)?; } - Token::QuotedString(ref value) => serialize_string(&**value, dest)?, + Token::QuotedString(ref value) => serialize_string(value, dest)?, Token::UnquotedUrl(ref value) => { dest.write_str("url(")?; - serialize_unquoted_url(&**value, dest)?; + serialize_unquoted_url(value, dest)?; dest.write_str(")")?; } Token::Delim(value) => dest.write_char(value)?, @@ -134,7 +132,7 @@ impl<'a> ToCss for Token<'a> { Token::CDC => dest.write_str("-->")?, Token::Function(ref name) => { - serialize_identifier(&**name, dest)?; + serialize_identifier(name, dest)?; dest.write_str("(")?; } Token::ParenthesisBlock => dest.write_str("(")?, @@ -167,7 +165,7 @@ fn hex_escape(ascii_byte: u8, dest: &mut W) -> fmt::Result where W: fmt::Write, { - static HEX_DIGITS: &'static [u8; 16] = b"0123456789abcdef"; + static HEX_DIGITS: &[u8; 16] = b"0123456789abcdef"; let b3; let b4; let bytes = if ascii_byte > 0x0F { @@ -179,7 +177,7 @@ where b3 = [b'\\', HEX_DIGITS[ascii_byte as usize], b' ']; &b3[..] }; - dest.write_str(unsafe { str::from_utf8_unchecked(&bytes) }) + dest.write_str(unsafe { str::from_utf8_unchecked(bytes) }) } fn char_escape(ascii_byte: u8, dest: &mut W) -> fmt::Result @@ -199,9 +197,9 @@ where return Ok(()); } - if value.starts_with("--") { + if let Some(value) = value.strip_prefix("--") { dest.write_str("--")?; - serialize_name(&value[2..], dest) + serialize_name(value, dest) } else if value == "-" { dest.write_str("\\-") } else { @@ -240,7 +238,7 @@ where dest.write_str(&value[chunk_start..i])?; if let Some(escaped) = escaped { dest.write_str(escaped)?; - } else if (b >= b'\x01' && b <= b'\x1F') || b == b'\x7F' { + } else if (b'\x01'..=b'\x1F').contains(&b) || b == b'\x7F' { hex_escape(b, dest)?; } else { char_escape(b, dest)?; diff --git a/src/tests.rs b/src/tests.rs index f9dea193..98c51f61 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -5,8 +5,7 @@ #[cfg(feature = "bench")] extern crate test; -use encoding_rs; -use serde_json::{self, json, Map, Value}; +use serde_json::{json, Map, Value}; #[cfg(feature = "bench")] use self::test::Bencher; @@ -25,25 +24,23 @@ macro_rules! JArray { } fn almost_equals(a: &Value, b: &Value) -> bool { - match (a, b) { - (&Value::Number(ref a), &Value::Number(ref b)) => { + let var_name = match (a, b) { + (Value::Number(a), Value::Number(b)) => { let a = a.as_f64().unwrap(); let b = b.as_f64().unwrap(); (a - b).abs() <= a.abs() * 1e-6 } (&Value::Bool(a), &Value::Bool(b)) => a == b, - (&Value::String(ref a), &Value::String(ref b)) => a == b, - (&Value::Array(ref a), &Value::Array(ref b)) => { - a.len() == b.len() - && a.iter() - .zip(b.iter()) - .all(|(ref a, ref b)| almost_equals(*a, *b)) + (Value::String(a), Value::String(b)) => a == b, + (Value::Array(a), Value::Array(b)) => { + a.len() == b.len() && a.iter().zip(b.iter()).all(|(a, b)| almost_equals(a, b)) } (&Value::Object(_), &Value::Object(_)) => panic!("Not implemented"), (&Value::Null, &Value::Null) => true, _ => false, - } + }; + var_name } fn normalize(json: &mut Value) { @@ -77,7 +74,7 @@ fn assert_json_eq(results: Value, mut expected: Value, message: &str) { } } -fn run_raw_json_tests ()>(json_data: &str, run: F) { +fn run_raw_json_tests(json_data: &str, run: F) { let items = match serde_json::from_str(json_data) { Ok(Value::Array(items)) => items, other => panic!("Invalid JSON: {:?}", other), @@ -242,7 +239,7 @@ fn stylesheet_from_bytes() { fn get_string<'a>(map: &'a Map, key: &str) -> Option<&'a str> { match map.get(key) { - Some(&Value::String(ref s)) => Some(s), + Some(Value::String(s)) => Some(s), Some(&Value::Null) => None, None => None, _ => panic!("Unexpected JSON"), @@ -393,7 +390,7 @@ fn unicode_range() { if input.is_exhausted() { Ok(result) } else { - while let Ok(_) = input.next() {} + while input.next().is_ok() {} Ok(None) } }); @@ -593,8 +590,6 @@ fn line_numbers() { #[test] fn overflow() { - use std::iter::repeat; - let css = r" 2147483646 2147483647 @@ -619,7 +614,7 @@ fn overflow() { -3.402824e+38 " - .replace("{309 zeros}", &repeat('0').take(309).collect::()); + .replace("{309 zeros}", &"0".repeat(309)); let mut input = ParserInput::new(&css); let mut input = Parser::new(&mut input); @@ -637,15 +632,13 @@ fn overflow() { assert_eq!(input.expect_integer(), Ok(-2147483648)); assert_eq!(input.expect_integer(), Ok(-2147483648)); - assert_eq!(input.expect_number(), Ok(3.30282347e+38)); + assert_eq!(input.expect_number(), Ok(3.302_823_5e38)); assert_eq!(input.expect_number(), Ok(f32::MAX)); assert_eq!(input.expect_number(), Ok(f32::INFINITY)); - assert!(f32::MAX != f32::INFINITY); - assert_eq!(input.expect_number(), Ok(-3.30282347e+38)); + assert_eq!(input.expect_number(), Ok(-3.302_823_5e38)); assert_eq!(input.expect_number(), Ok(f32::MIN)); assert_eq!(input.expect_number(), Ok(f32::NEG_INFINITY)); - assert!(f32::MIN != f32::NEG_INFINITY); } #[test] @@ -784,7 +777,7 @@ where impl<'a> ToJson for CowRcStr<'a> { fn to_json(&self) -> Value { - let s: &str = &*self; + let s: &str = self; s.to_json() } } @@ -847,7 +840,7 @@ fn no_stack_overflow_multiple_nested_blocks() { } let mut input = ParserInput::new(&input); let mut input = Parser::new(&mut input); - while let Ok(..) = input.next() {} + while input.next().is_ok() {} } impl<'i> DeclarationParser<'i> for JsonParser { @@ -870,11 +863,9 @@ impl<'i> DeclarationParser<'i> for JsonParser { // (even CSS Variables forbid top-level `!`) if token == Token::Delim('!') { input.reset(&start); - if parse_important(input).is_ok() { - if input.is_exhausted() { - important = true; - break; - } + if parse_important(input).is_ok() && input.is_exhausted() { + important = true; + break; } input.reset(&start); token = input.next_including_whitespace().unwrap().clone(); @@ -905,7 +896,7 @@ impl<'i> AtRuleParser<'i> for JsonParser { ]; match_ignore_ascii_case! { &*name, "charset" => { - Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone()).into())) + Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone()))) }, _ => Ok(prelude), } @@ -978,9 +969,9 @@ fn one_component_value_to_json(token: Token, input: &mut Parser) -> Value { fn numeric(value: f32, int_value: Option, has_sign: bool) -> Vec { vec![ Token::Number { - value: value, - int_value: int_value, - has_sign: has_sign, + value, + int_value, + has_sign, } .to_css_string() .to_json(), @@ -1137,7 +1128,7 @@ fn parse_until_before_stops_at_delimiter_or_end_of_input() { let ox = ix.next(); let oy = iy.next(); assert_eq!(ox, oy); - if let Err(_) = ox { + if ox.is_err() { break; } } @@ -1223,7 +1214,7 @@ fn parse_sourcemapping_comments() { for test in tests { let mut input = ParserInput::new(test.0); let mut parser = Parser::new(&mut input); - while let Ok(_) = parser.next_including_whitespace() {} + while parser.next_including_whitespace().is_ok() {} assert_eq!(parser.current_source_map_url(), test.1); } } @@ -1247,7 +1238,7 @@ fn parse_sourceurl_comments() { for test in tests { let mut input = ParserInput::new(test.0); let mut parser = Parser::new(&mut input); - while let Ok(_) = parser.next_including_whitespace() {} + while parser.next_including_whitespace().is_ok() {} assert_eq!(parser.current_source_url(), test.1); } } @@ -1321,7 +1312,8 @@ fn utf16_columns() { break; } Err(_) => { - assert!(false); + // should this be an explicit panic instead? + unreachable!(); } Ok(_) => {} }; diff --git a/src/tokenizer.rs b/src/tokenizer.rs index a3b70063..f1716c71 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -255,10 +255,10 @@ impl<'a> Tokenizer<'a> { #[inline] pub fn see_function(&mut self, name: &str) { - if self.var_or_env_functions == SeenStatus::LookingForThem { - if name.eq_ignore_ascii_case("var") || name.eq_ignore_ascii_case("env") { - self.var_or_env_functions = SeenStatus::SeenAtLeastOne; - } + if self.var_or_env_functions == SeenStatus::LookingForThem + && (name.eq_ignore_ascii_case("var") || name.eq_ignore_ascii_case("env")) + { + self.var_or_env_functions = SeenStatus::SeenAtLeastOne; } } @@ -322,10 +322,12 @@ impl<'a> Tokenizer<'a> { pub fn current_source_line(&self) -> &'a str { let current = self.position(); - let start = self.slice(SourcePosition(0)..current) + let start = self + .slice(SourcePosition(0)..current) .rfind(|c| matches!(c, '\r' | '\n' | '\x0C')) .map_or(0, |start| start + 1); - let end = self.slice(current..SourcePosition(self.input.len())) + let end = self + .slice(current..SourcePosition(self.input.len())) .find(|c| matches!(c, '\r' | '\n' | '\x0C')) .map_or(self.input.len(), |end| current.0 + end); self.slice(SourcePosition(start)..SourcePosition(end)) @@ -424,7 +426,10 @@ impl<'a> Tokenizer<'a> { #[inline] fn next_char(&self) -> char { - unsafe { self.input.get_unchecked(self.position().0..) }.chars().next().unwrap() + unsafe { self.input.get_unchecked(self.position().0..) } + .chars() + .next() + .unwrap() } // Given that a newline has been seen, advance over the newline @@ -582,11 +587,11 @@ fn next_token<'a>(tokenizer: &mut Tokenizer<'a>) -> Result, ()> { b'+' => { if ( tokenizer.has_at_least(1) - && matches!(tokenizer.byte_at(1), b'0'..=b'9') + && tokenizer.byte_at(1).is_ascii_digit() ) || ( tokenizer.has_at_least(2) && tokenizer.byte_at(1) == b'.' - && matches!(tokenizer.byte_at(2), b'0'..=b'9') + && tokenizer.byte_at(2).is_ascii_digit() ) { consume_numeric(tokenizer) } else { @@ -598,11 +603,11 @@ fn next_token<'a>(tokenizer: &mut Tokenizer<'a>) -> Result, ()> { b'-' => { if ( tokenizer.has_at_least(1) - && matches!(tokenizer.byte_at(1), b'0'..=b'9') + && tokenizer.byte_at(1).is_ascii_digit() ) || ( tokenizer.has_at_least(2) && tokenizer.byte_at(1) == b'.' - && matches!(tokenizer.byte_at(2), b'0'..=b'9') + && tokenizer.byte_at(2).is_ascii_digit() ) { consume_numeric(tokenizer) } else if tokenizer.starts_with(b"-->") { @@ -617,8 +622,7 @@ fn next_token<'a>(tokenizer: &mut Tokenizer<'a>) -> Result, ()> { }, b'.' => { if tokenizer.has_at_least(1) - && matches!(tokenizer.byte_at(1), b'0'..=b'9' - ) { + && tokenizer.byte_at(1).is_ascii_digit() { consume_numeric(tokenizer) } else { tokenizer.advance(1); @@ -1001,7 +1005,7 @@ fn byte_to_hex_digit(b: u8) -> Option { } fn byte_to_decimal_digit(b: u8) -> Option { - if b >= b'0' && b <= b'9' { + if b.is_ascii_digit() { Some((b - b'0') as u32) } else { None @@ -1038,7 +1042,7 @@ fn consume_numeric<'a>(tokenizer: &mut Tokenizer<'a>) -> Token<'a> { let mut fractional_part: f64 = 0.; if tokenizer.has_at_least(1) && tokenizer.next_byte_unchecked() == b'.' - && matches!(tokenizer.byte_at(1), b'0'..=b'9') + && tokenizer.byte_at(1).is_ascii_digit() { is_integer = false; tokenizer.advance(1); // Consume '.' @@ -1055,32 +1059,32 @@ fn consume_numeric<'a>(tokenizer: &mut Tokenizer<'a>) -> Token<'a> { let mut value = sign * (integral_part + fractional_part); - if tokenizer.has_at_least(1) && matches!(tokenizer.next_byte_unchecked(), b'e' | b'E') { - if matches!(tokenizer.byte_at(1), b'0'..=b'9') + if tokenizer.has_at_least(1) + && matches!(tokenizer.next_byte_unchecked(), b'e' | b'E') + && (tokenizer.byte_at(1).is_ascii_digit() || (tokenizer.has_at_least(2) && matches!(tokenizer.byte_at(1), b'+' | b'-') - && matches!(tokenizer.byte_at(2), b'0'..=b'9')) - { - is_integer = false; + && tokenizer.byte_at(2).is_ascii_digit())) + { + is_integer = false; + tokenizer.advance(1); + let (has_sign, sign) = match tokenizer.next_byte_unchecked() { + b'-' => (true, -1.), + b'+' => (true, 1.), + _ => (false, 1.), + }; + if has_sign { tokenizer.advance(1); - let (has_sign, sign) = match tokenizer.next_byte_unchecked() { - b'-' => (true, -1.), - b'+' => (true, 1.), - _ => (false, 1.), - }; - if has_sign { - tokenizer.advance(1); - } - let mut exponent: f64 = 0.; - while let Some(digit) = byte_to_decimal_digit(tokenizer.next_byte_unchecked()) { - exponent = exponent * 10. + digit as f64; - tokenizer.advance(1); - if tokenizer.is_eof() { - break; - } + } + let mut exponent: f64 = 0.; + while let Some(digit) = byte_to_decimal_digit(tokenizer.next_byte_unchecked()) { + exponent = exponent * 10. + digit as f64; + tokenizer.advance(1); + if tokenizer.is_eof() { + break; } - value *= f64::powf(10., sign * exponent); } + value *= f64::powf(10., sign * exponent); } let int_value = if is_integer { @@ -1339,7 +1343,7 @@ fn consume_unquoted_url<'a>(tokenizer: &mut Tokenizer<'a>) -> Result, } // (value, number of digits up to 6) -fn consume_hex_digits<'a>(tokenizer: &mut Tokenizer<'a>) -> (u32, u32) { +fn consume_hex_digits(tokenizer: &mut Tokenizer<'_>) -> (u32, u32) { let mut value = 0; let mut digits = 0; while digits < 6 && !tokenizer.is_eof() { diff --git a/src/unicode_range.rs b/src/unicode_range.rs index b0a2017c..ce6bb3b5 100644 --- a/src/unicode_range.rs +++ b/src/unicode_range.rs @@ -24,7 +24,7 @@ pub struct UnicodeRange { impl UnicodeRange { /// https://drafts.csswg.org/css-syntax/#urange-syntax - pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { + pub fn parse<'i>(input: &mut Parser<'i, '_>) -> Result> { // = // u '+' '?'* | // u '?'* | @@ -57,7 +57,7 @@ impl UnicodeRange { } } -fn parse_tokens<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), BasicParseError<'i>> { +fn parse_tokens<'i>(input: &mut Parser<'i, '_>) -> Result<(), BasicParseError<'i>> { match input.next_including_whitespace()?.clone() { Token::Delim('+') => { match *input.next_including_whitespace()? { @@ -123,15 +123,13 @@ fn parse_concatenated(text: &[u8]) -> Result { start: first_hex_value, end: first_hex_value, }); - } else { - if let Some((&b'-', mut text)) = text.split_first() { - let (second_hex_value, hex_digit_count) = consume_hex(&mut text); - if hex_digit_count > 0 && hex_digit_count <= 6 && text.is_empty() { - return Ok(UnicodeRange { - start: first_hex_value, - end: second_hex_value, - }); - } + } else if let Some((&b'-', mut text)) = text.split_first() { + let (second_hex_value, hex_digit_count) = consume_hex(&mut text); + if hex_digit_count > 0 && hex_digit_count <= 6 && text.is_empty() { + return Ok(UnicodeRange { + start: first_hex_value, + end: second_hex_value, + }); } } Err(()) From 50b3203748b22d159eabbf085783d78c764cea1f Mon Sep 17 00:00:00 2001 From: Chipnertkj <32001812+chipnertkj@users.noreply.github.com> Date: Thu, 7 Dec 2023 02:01:45 +0100 Subject: [PATCH 05/21] Make color use serde, and update serialization to be derived. --- Cargo.toml | 8 +-- color/Cargo.toml | 6 ++- color/lib.rs | 123 +++++------------------------------------------ src/color.rs | 2 + 4 files changed, 23 insertions(+), 116 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c7beb4c1..ea04cb43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cssparser" version = "0.33.0" -authors = [ "Simon Sapin " ] +authors = ["Simon Sapin "] description = "Rust implementation of CSS Syntax Level 3" documentation = "https://docs.rs/cssparser/" @@ -20,11 +20,11 @@ difference = "2.0" encoding_rs = "0.8" [dependencies] -cssparser-macros = {path = "./macros", version = "0.6.1"} +cssparser-macros = { path = "./macros", version = "0.6.1" } dtoa-short = "0.3" itoa = "1.0" -phf = {version = "0.11.2", features = ["macros"]} -serde = {version = "1.0", optional = true} +phf = { version = "0.11.2", features = ["macros"] } +serde = { version = "1.0", features = ["derive"], optional = true } smallvec = "1.0" [features] diff --git a/color/Cargo.toml b/color/Cargo.toml index d1e315a8..47544815 100644 --- a/color/Cargo.toml +++ b/color/Cargo.toml @@ -12,7 +12,11 @@ edition = "2021" path = "lib.rs" [dependencies] -cssparser = { version = "0.33", path = ".." } +cssparser = { path = ".." } +serde = { version = "1.0", features = ["derive"], optional = true } + +[features] +serde = ["cssparser/serde", "dep:serde"] [dev-dependencies] serde_json = "1.0.25" diff --git a/color/lib.rs b/color/lib.rs index 1c52e38d..cf30fd5b 100644 --- a/color/lib.rs +++ b/color/lib.rs @@ -16,8 +16,6 @@ use cssparser::color::{ PredefinedColorSpace, OPAQUE, }; use cssparser::{match_ignore_ascii_case, CowRcStr, ParseError, Parser, ToCss, Token}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::f32::consts::PI; use std::fmt; use std::str::FromStr; @@ -55,10 +53,8 @@ where let token = input.next()?; match *token { Token::Hash(ref value) | Token::IDHash(ref value) => { - parse_hash_color(value.as_bytes()).map(|(r, g, b, a)| { - P::Output::from_rgba(r, g, b, a) - }) - }, + parse_hash_color(value.as_bytes()).map(|(r, g, b, a)| P::Output::from_rgba(r, g, b, a)) + } Token::Ident(ref value) => parse_color_keyword(value), Token::Function(ref name) => { let name = name.clone(); @@ -506,6 +502,7 @@ fn normalize_hue(hue: f32) -> f32 { } /// A color with red, green, blue, and alpha components, in a byte each. +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Clone, Copy, PartialEq, Debug)] pub struct RgbaLegacy { /// The red component. @@ -544,27 +541,6 @@ impl RgbaLegacy { } } -#[cfg(feature = "serde")] -impl Serialize for RgbaLegacy { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - (self.red, self.green, self.blue, self.alpha).serialize(serializer) - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for RgbaLegacy { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let (r, g, b, a) = Deserialize::deserialize(deserializer)?; - Ok(RgbaLegacy::new(r, g, b, a)) - } -} - impl ToCss for RgbaLegacy { fn to_css(&self, dest: &mut W) -> fmt::Result where @@ -588,6 +564,7 @@ impl ToCss for RgbaLegacy { /// Color specified by hue, saturation and lightness components. #[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Hsl { /// The hue component. pub hue: Option, @@ -632,29 +609,9 @@ impl ToCss for Hsl { } } -#[cfg(feature = "serde")] -impl Serialize for Hsl { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - (self.hue, self.saturation, self.lightness, self.alpha).serialize(serializer) - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Hsl { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let (lightness, a, b, alpha) = Deserialize::deserialize(deserializer)?; - Ok(Self::new(lightness, a, b, alpha)) - } -} - /// Color specified by hue, whiteness and blackness components. #[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Hwb { /// The hue component. pub hue: Option, @@ -699,32 +656,12 @@ impl ToCss for Hwb { } } -#[cfg(feature = "serde")] -impl Serialize for Hwb { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - (self.hue, self.whiteness, self.blackness, self.alpha).serialize(serializer) - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Hwb { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let (lightness, whiteness, blackness, alpha) = Deserialize::deserialize(deserializer)?; - Ok(Self::new(lightness, whiteness, blackness, alpha)) - } -} - // NOTE: LAB and OKLAB is not declared inside the [impl_lab_like] macro, // because it causes cbindgen to ignore them. /// Color specified by lightness, a- and b-axis components. #[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Lab { /// The lightness component. pub lightness: Option, @@ -738,6 +675,7 @@ pub struct Lab { /// Color specified by lightness, a- and b-axis components. #[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Oklab { /// The lightness component. pub lightness: Option, @@ -768,27 +706,6 @@ macro_rules! impl_lab_like { } } - #[cfg(feature = "serde")] - impl Serialize for $cls { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - (self.lightness, self.a, self.b, self.alpha).serialize(serializer) - } - } - - #[cfg(feature = "serde")] - impl<'de> Deserialize<'de> for $cls { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let (lightness, a, b, alpha) = Deserialize::deserialize(deserializer)?; - Ok(Self::new(lightness, a, b, alpha)) - } - } - impl ToCss for $cls { fn to_css(&self, dest: &mut W) -> fmt::Result where @@ -816,6 +733,7 @@ impl_lab_like!(Oklab, "oklab"); /// Color specified by lightness, chroma and hue components. #[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Lch { /// The lightness component. pub lightness: Option, @@ -829,6 +747,7 @@ pub struct Lch { /// Color specified by lightness, chroma and hue components. #[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Oklch { /// The lightness component. pub lightness: Option, @@ -859,27 +778,6 @@ macro_rules! impl_lch_like { } } - #[cfg(feature = "serde")] - impl Serialize for $cls { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - (self.lightness, self.chroma, self.hue, self.alpha).serialize(serializer) - } - } - - #[cfg(feature = "serde")] - impl<'de> Deserialize<'de> for $cls { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let (lightness, chroma, hue, alpha) = Deserialize::deserialize(deserializer)?; - Ok(Self::new(lightness, chroma, hue, alpha)) - } - } - impl ToCss for $cls { fn to_css(&self, dest: &mut W) -> fmt::Result where @@ -905,6 +803,7 @@ impl_lch_like!(Oklch, "oklch"); /// A color specified by the color() function. /// #[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct ColorFunction { /// The color space for this color. pub color_space: PredefinedColorSpace, @@ -966,6 +865,8 @@ impl ToCss for ColorFunction { /// /// #[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(tag = "type"))] pub enum Color { /// The 'currentcolor' keyword. CurrentColor, diff --git a/src/color.rs b/src/color.rs index d5f9a5c0..f4d11809 100644 --- a/src/color.rs +++ b/src/color.rs @@ -77,6 +77,8 @@ pub fn serialize_color_alpha( /// A Predefined color space specified in: /// #[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(tag = "type"))] pub enum PredefinedColorSpace { /// Srgb, From d44a7ccf35c83f7fee3c214d9e6015efa80b5df0 Mon Sep 17 00:00:00 2001 From: Tiaan Louw Date: Sat, 6 Apr 2024 19:08:42 +0200 Subject: [PATCH 06/21] Fix the documentation of what certain parse functions return (#372) --- src/parser.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 424306e1..b1357f26 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -677,7 +677,7 @@ impl<'i: 't, 't> Parser<'i, 't> { } /// Have the given closure parse something, then check the the input is exhausted. - /// The result is overridden to `Err(())` if some input remains. + /// The result is overridden to an `Err(..)` if some input remains. /// /// This can help tell e.g. `color: green;` from `color: green 4px;` #[inline] @@ -767,7 +767,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// The given closure is called with a "delimited" parser /// that stops at the end of the block or function (at the matching closing token). /// - /// The result is overridden to `Err(())` if the closure leaves some input before that point. + /// The result is overridden to an `Err(..)` if the closure leaves some input before that point. #[inline] pub fn parse_nested_block(&mut self, parse: F) -> Result> where @@ -783,7 +783,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// that stops before the first character at this block/function nesting level /// that matches the given set of delimiters, or at the end of the input. /// - /// The result is overridden to `Err(())` if the closure leaves some input before that point. + /// The result is overridden to an `Err(..)` if the closure leaves some input before that point. #[inline] pub fn parse_until_before( &mut self, From 22e42797d21a21cab414eaafc2240632bee99816 Mon Sep 17 00:00:00 2001 From: Alex Touchet Date: Sun, 7 Apr 2024 05:31:02 -0700 Subject: [PATCH 07/21] Update Servo style crate URL (#379) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 84d47d9e..d9ca4ada 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ rust-cssparser [![Build Status](https://github.com/servo/rust-cssparser/actions/workflows/main.yml/badge.svg)](https://github.com/servo/rust-cssparser/actions) -[Documentation](https://docs.rs/cssparser/) +[Documentation](https://docs.rs/cssparser) Rust implementation of [CSS Syntax Module Level 3](https://drafts.csswg.org/css-syntax/) @@ -53,5 +53,5 @@ Parsing CSS involves a series of steps: It does however provide some helper functions to parse [CSS colors](src/color.rs) and [An+B](src/nth.rs) (the argument to `:nth-child()` and related selectors. - See [Servo’s `style` crate](https://github.com/servo/servo/tree/master/components/style) + See [Servo’s `style` crate](https://github.com/servo/stylo/tree/main/style) for an example of a parser based on rust-cssparser. From e95c5be76ef12f44e258f5c021b937d069e16b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Mon, 8 Apr 2024 00:03:00 +0200 Subject: [PATCH 08/21] Add a profiling cargo profile. --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index ea04cb43..6630f7f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,10 @@ phf = { version = "0.11.2", features = ["macros"] } serde = { version = "1.0", features = ["derive"], optional = true } smallvec = "1.0" +[profile.profiling] +inherits = "release" +debug = true + [features] bench = [] dummy_match_byte = [] From 8f13fc9689af200b2bd04ba6ea15a1bc1fe29090 Mon Sep 17 00:00:00 2001 From: Tiaan Louw Date: Mon, 8 Apr 2024 00:04:27 +0200 Subject: [PATCH 09/21] Further clarifications in docs for what errors are returned. (#380) Based on #281 --- src/nth.rs | 2 +- src/parser.rs | 2 +- src/rules_and_declarations.rs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/nth.rs b/src/nth.rs index e4bde0de..4fe5a6bc 100644 --- a/src/nth.rs +++ b/src/nth.rs @@ -7,7 +7,7 @@ use super::{BasicParseError, Parser, ParserInput, Token}; /// Parse the *An+B* notation, as found in the `:nth-child()` selector. /// The input is typically the arguments of a function, /// in which case the caller needs to check if the arguments’ parser is exhausted. -/// Return `Ok((A, B))`, or `Err(())` for a syntax error. +/// Return `Ok((A, B))`, or an `Err(..)` for a syntax error. pub fn parse_nth<'i>(input: &mut Parser<'i, '_>) -> Result<(i32, i32), BasicParseError<'i>> { match *input.next()? { Token::Number { diff --git a/src/parser.rs b/src/parser.rs index b1357f26..77a21008 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -698,7 +698,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// /// Successful results are accumulated in a vector. /// - /// This method returns `Err(())` the first time that a closure call does, + /// This method returns an`Err(..)` the first time that a closure call does, /// or if a closure call leaves some input before the next comma or the end /// of the input. #[inline] diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index b23e9dc0..c6af2311 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -34,7 +34,7 @@ pub trait DeclarationParser<'i> { /// /// Return the finished representation for the declaration /// as returned by `DeclarationListParser::next`, - /// or `Err(())` to ignore the entire declaration as invalid. + /// or an `Err(..)` to ignore the entire declaration as invalid. /// /// Declaration name matching should be case-insensitive in the ASCII range. /// This can be done with `std::ascii::Ascii::eq_ignore_ascii_case`, @@ -78,7 +78,7 @@ pub trait AtRuleParser<'i> { /// Parse the prelude of an at-rule with the given `name`. /// /// Return the representation of the prelude and the type of at-rule, - /// or `Err(())` to ignore the entire at-rule as invalid. + /// or an `Err(..)` to ignore the entire at-rule as invalid. /// /// The prelude is the part after the at-keyword /// and before the `;` semicolon or `{ /* ... */ }` block. @@ -122,7 +122,7 @@ pub trait AtRuleParser<'i> { /// /// Return the finished representation of the at-rule /// as returned by `RuleListParser::next` or `DeclarationListParser::next`, - /// or `Err(())` to ignore the entire at-rule as invalid. + /// or an `Err(..)` to ignore the entire at-rule as invalid. /// /// This is only called when `parse_prelude` returned `WithBlock`, and a block /// was indeed found following the prelude. @@ -161,7 +161,7 @@ pub trait QualifiedRuleParser<'i> { /// Parse the prelude of a qualified rule. For style rules, this is as Selector list. /// /// Return the representation of the prelude, - /// or `Err(())` to ignore the entire at-rule as invalid. + /// or an `Err(..)` to ignore the entire at-rule as invalid. /// /// The prelude is the part before the `{ /* ... */ }` block. /// @@ -180,7 +180,7 @@ pub trait QualifiedRuleParser<'i> { /// /// Return the finished representation of the qualified rule /// as returned by `RuleListParser::next`, - /// or `Err(())` to ignore the entire at-rule as invalid. + /// or an `Err(..)` to ignore the entire at-rule as invalid. fn parse_block<'t>( &mut self, prelude: Self::Prelude, @@ -353,7 +353,7 @@ where } } -/// `RuleListParser` is an iterator that yields `Ok(_)` for a rule or `Err(())` for an invalid one. +/// `RuleListParser` is an iterator that yields `Ok(_)` for a rule or an `Err(..)` for an invalid one. impl<'i, 't, 'a, R, P, E: 'i> Iterator for StyleSheetParser<'i, 't, 'a, P> where P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E> From ca69d958968ae6f15aa6a921242e1d2c052254a4 Mon Sep 17 00:00:00 2001 From: Tiaan Louw Date: Tue, 16 Apr 2024 18:08:01 +0200 Subject: [PATCH 10/21] Add cargo fmt and cargo clippy checks to CI (#384) --- .github/workflows/main.yml | 7 +++++-- color/lib.rs | 1 + src/color.rs | 4 +++- src/parser.rs | 3 ++- src/rules_and_declarations.rs | 1 + src/serializer.rs | 4 ++-- src/tests.rs | 10 ++++------ src/tokenizer.rs | 8 ++++---- 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 07bedca5..b08ddef7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,7 +7,7 @@ on: workflow_dispatch: merge_group: types: [checks_requested] - + jobs: linux-ci: name: Linux @@ -36,7 +36,10 @@ jobs: profile: minimal toolchain: ${{ matrix.toolchain }} override: true - components: ${{ matrix.toolchain == 'nightly' && 'miri,rust-src' || '' }} + components: rustfmt, clippy, ${{ matrix.toolchain == 'nightly' && 'miri,rust-src' || '' }} + + - name: Cargo format & lint + run: cargo fmt --check && cargo clippy -- -Dwarnings - name: Cargo build run: cargo build ${{ matrix.features }} diff --git a/color/lib.rs b/color/lib.rs index cf30fd5b..83d0f279 100644 --- a/color/lib.rs +++ b/color/lib.rs @@ -25,6 +25,7 @@ use std::str::FromStr; /// Matching is case-insensitive in the ASCII range. /// CSS escaping (if relevant) should be resolved before calling this function. /// (For example, the value of an `Ident` token is fine.) +#[allow(clippy::result_unit_err)] #[inline] pub fn parse_color_keyword(ident: &str) -> Result where diff --git a/src/color.rs b/src/color.rs index f4d11809..e3ae926d 100644 --- a/src/color.rs +++ b/src/color.rs @@ -76,7 +76,7 @@ pub fn serialize_color_alpha( /// A Predefined color space specified in: /// -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, Copy, Eq, PartialEq, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", serde(tag = "type"))] pub enum PredefinedColorSpace { @@ -143,6 +143,7 @@ impl ToCss for PredefinedColorSpace { } /// Parse a color hash, without the leading '#' character. +#[allow(clippy::result_unit_err)] #[inline] pub fn parse_hash_color(value: &[u8]) -> Result<(u8, u8, u8, f32), ()> { Ok(match value.len() { @@ -330,6 +331,7 @@ ascii_case_insensitive_phf_map! { /// Returns the named color with the given name. /// +#[allow(clippy::result_unit_err)] #[inline] pub fn parse_named_color(ident: &str) -> Result<(u8, u8, u8), ()> { named_colors::get(ident).copied().ok_or(()) diff --git a/src/parser.rs b/src/parser.rs index 77a21008..e65cb013 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -53,7 +53,7 @@ impl ParserState { /// /// Would need to scan the whole {} block to find a semicolon, only for parsing getting restarted /// as a qualified rule later. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ParseUntilErrorBehavior { /// Consume until we see the relevant delimiter or the end of the stream. Consume, @@ -606,6 +606,7 @@ impl<'i: 't, 't> Parser<'i, 't> { /// See the `Parser::parse_nested_block` method to parse the content of functions or blocks. /// /// This only returns a closing token when it is unmatched (and therefore an error). + #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Result<&Token<'i>, BasicParseError<'i>> { self.skip_whitespace(); self.next_including_whitespace_and_comments() diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index c6af2311..9240f6de 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -106,6 +106,7 @@ pub trait AtRuleParser<'i> { /// This is only called when `parse_prelude` returned `WithoutBlock`, and /// either the `;` semicolon indeed follows the prelude, or parser is at /// the end of the input. + #[allow(clippy::result_unit_err)] fn rule_without_block( &mut self, prelude: Self::Prelude, diff --git a/src/serializer.rs b/src/serializer.rs index 403976cd..3c6e31cb 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -338,7 +338,7 @@ where macro_rules! impl_tocss_for_int { ($T: ty) => { - impl<'a> ToCss for $T { + impl ToCss for $T { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write, @@ -361,7 +361,7 @@ impl_tocss_for_int!(u64); macro_rules! impl_tocss_for_float { ($T: ty) => { - impl<'a> ToCss for $T { + impl ToCss for $T { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write, diff --git a/src/tests.rs b/src/tests.rs index 98c51f61..7389664d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -430,11 +430,9 @@ fn serializer(preserve_comments: bool) { preserve_comments: bool, ) { while let Ok(token) = if preserve_comments { - input - .next_including_whitespace_and_comments() - .map(|t| t.clone()) + input.next_including_whitespace_and_comments().cloned() } else { - input.next_including_whitespace().map(|t| t.clone()) + input.next_including_whitespace().cloned() } { let token_type = token.serialization_type(); if !preserve_comments && previous_token.needs_separator_when_before(token_type) @@ -856,7 +854,7 @@ impl<'i> DeclarationParser<'i> for JsonParser { let mut important = false; loop { let start = input.state(); - if let Ok(mut token) = input.next_including_whitespace().map(|t| t.clone()) { + if let Ok(mut token) = input.next_including_whitespace().cloned() { // Hack to deal with css-parsing-tests assuming that // `!important` in the middle of a declaration value is OK. // This can never happen per spec @@ -959,7 +957,7 @@ impl<'i> RuleBodyItemParser<'i, Value, ()> for JsonParser { fn component_values_to_json(input: &mut Parser) -> Vec { let mut values = vec![]; - while let Ok(token) = input.next_including_whitespace().map(|t| t.clone()) { + while let Ok(token) = input.next_including_whitespace().cloned() { values.push(one_component_value_to_json(token, input)); } values diff --git a/src/tokenizer.rs b/src/tokenizer.rs index f1716c71..ea173a5e 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -566,11 +566,11 @@ fn next_token<'a>(tokenizer: &mut Tokenizer<'a>) -> Result, ()> { b'#' => { tokenizer.advance(1); if is_ident_start(tokenizer) { IDHash(consume_name(tokenizer)) } - else if !tokenizer.is_eof() && match tokenizer.next_byte_unchecked() { + else if !tokenizer.is_eof() && + matches!(tokenizer.next_byte_unchecked(), b'0'..=b'9' | b'-') { // Any other valid case here already resulted in IDHash. - b'0'..=b'9' | b'-' => true, - _ => false, - } { Hash(consume_name(tokenizer)) } + Hash(consume_name(tokenizer)) + } else { Delim('#') } }, b'$' => { From cda06eede1c32af105add4017fbe1b651319f334 Mon Sep 17 00:00:00 2001 From: Tiaan Louw Date: Tue, 16 Apr 2024 19:02:53 +0200 Subject: [PATCH 11/21] Make PredefinedColorSpace follow the convention of parse() and ToCss (#386) Also bump version to 0.34, because this is a breaking change. --- Cargo.toml | 4 ++-- color/lib.rs | 9 +------- color/tests.rs | 2 +- src/color.rs | 57 ++++++++++++++++++++++---------------------------- 4 files changed, 29 insertions(+), 43 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6630f7f4..f783f19f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser" -version = "0.33.0" +version = "0.34.0" authors = ["Simon Sapin "] description = "Rust implementation of CSS Syntax Level 3" @@ -23,7 +23,7 @@ encoding_rs = "0.8" cssparser-macros = { path = "./macros", version = "0.6.1" } dtoa-short = "0.3" itoa = "1.0" -phf = { version = "0.11.2", features = ["macros"] } +phf = { version = "0.11.2", features = ["macros"] } serde = { version = "1.0", features = ["derive"], optional = true } smallvec = "1.0" diff --git a/color/lib.rs b/color/lib.rs index 83d0f279..5b116c23 100644 --- a/color/lib.rs +++ b/color/lib.rs @@ -18,7 +18,6 @@ use cssparser::color::{ use cssparser::{match_ignore_ascii_case, CowRcStr, ParseError, Parser, ToCss, Token}; use std::f32::consts::PI; use std::fmt; -use std::str::FromStr; /// Return the named color with the given name. /// @@ -402,13 +401,7 @@ fn parse_color_with_color_space<'i, 't, P>( where P: ColorParser<'i>, { - let color_space = { - let location = arguments.current_source_location(); - - let ident = arguments.expect_ident()?; - PredefinedColorSpace::from_str(ident) - .map_err(|_| location.new_unexpected_token_error(Token::Ident(ident.clone())))? - }; + let color_space = PredefinedColorSpace::parse(arguments)?; let (c1, c2, c3, alpha) = parse_components( color_parser, diff --git a/color/tests.rs b/color/tests.rs index 85eb430e..d8d8e5d4 100644 --- a/color/tests.rs +++ b/color/tests.rs @@ -215,7 +215,7 @@ impl ToJson for Color { Color::Oklab(ref c) => json!([c.lightness, c.a, c.b, c.alpha]), Color::Oklch(ref c) => json!([c.lightness, c.chroma, c.hue, c.alpha]), Color::ColorFunction(ref c) => { - json!([c.color_space.as_str(), c.c1, c.c2, c.c3, c.alpha]) + json!([c.color_space.to_css_string(), c.c1, c.c2, c.c3, c.alpha]) } } } diff --git a/src/color.rs b/src/color.rs index e3ae926d..978936e0 100644 --- a/src/color.rs +++ b/src/color.rs @@ -14,9 +14,8 @@ /// The opaque alpha value of 1.0. pub const OPAQUE: f32 = 1.0; -use crate::ToCss; +use crate::{BasicParseError, Parser, ToCss, Token}; use std::fmt; -use std::str::FromStr; /// Clamp a 0..1 number to a 0..255 range to u8. /// @@ -99,36 +98,21 @@ pub enum PredefinedColorSpace { } impl PredefinedColorSpace { - /// Returns the string value of the predefined color space. - pub fn as_str(&self) -> &str { - match self { - PredefinedColorSpace::Srgb => "srgb", - PredefinedColorSpace::SrgbLinear => "srgb-linear", - PredefinedColorSpace::DisplayP3 => "display-p3", - PredefinedColorSpace::A98Rgb => "a98-rgb", - PredefinedColorSpace::ProphotoRgb => "prophoto-rgb", - PredefinedColorSpace::Rec2020 => "rec2020", - PredefinedColorSpace::XyzD50 => "xyz-d50", - PredefinedColorSpace::XyzD65 => "xyz-d65", - } - } -} - -impl FromStr for PredefinedColorSpace { - type Err = (); + /// Parse a PredefinedColorSpace from the given input. + pub fn parse<'i>(input: &mut Parser<'i, '_>) -> Result> { + let location = input.current_source_location(); - fn from_str(s: &str) -> Result { - Ok(match_ignore_ascii_case! { s, - "srgb" => PredefinedColorSpace::Srgb, - "srgb-linear" => PredefinedColorSpace::SrgbLinear, - "display-p3" => PredefinedColorSpace::DisplayP3, - "a98-rgb" => PredefinedColorSpace::A98Rgb, - "prophoto-rgb" => PredefinedColorSpace::ProphotoRgb, - "rec2020" => PredefinedColorSpace::Rec2020, - "xyz-d50" => PredefinedColorSpace::XyzD50, - "xyz" | "xyz-d65" => PredefinedColorSpace::XyzD65, - - _ => return Err(()), + let ident = input.expect_ident()?; + Ok(match_ignore_ascii_case! { ident, + "srgb" => Self::Srgb, + "srgb-linear" => Self::SrgbLinear, + "display-p3" => Self::DisplayP3, + "a98-rgb" => Self::A98Rgb, + "prophoto-rgb" => Self::ProphotoRgb, + "rec2020" => Self::Rec2020, + "xyz-d50" => Self::XyzD50, + "xyz" | "xyz-d65" => Self::XyzD65, + _ => return Err(location.new_basic_unexpected_token_error(Token::Ident(ident.clone()))), }) } } @@ -138,7 +122,16 @@ impl ToCss for PredefinedColorSpace { where W: fmt::Write, { - dest.write_str(self.as_str()) + dest.write_str(match self { + Self::Srgb => "srgb", + Self::SrgbLinear => "srgb-linear", + Self::DisplayP3 => "display-p3", + Self::A98Rgb => "a98-rgb", + Self::ProphotoRgb => "prophoto-rgb", + Self::Rec2020 => "rec2020", + Self::XyzD50 => "xyz-d50", + Self::XyzD65 => "xyz-d65", + }) } } From b75ce6a8df2dbd712fac9d49ba38ee09b96d0d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 17 Apr 2024 22:46:32 +0200 Subject: [PATCH 12/21] parser: Implement recent syntax spec change to avoid parsing custom properties that look like selectors. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements the: > If the first two non- values of rule’s prelude are > an whose value starts with "--" followed by a > , then... From https://drafts.csswg.org/css-syntax/#consume-qualified-rule Mozilla Bug: 1884879 Differential Revision: https://phabricator.services.mozilla.com/D207796 --- src/parser.rs | 26 ++++++++++------ src/rules_and_declarations.rs | 58 ++++++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index e65cb013..dd35fc50 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -117,8 +117,14 @@ impl SourceLocation { /// Create a new BasicParseError at this location for an unexpected token #[inline] pub fn new_basic_unexpected_token_error(self, token: Token<'_>) -> BasicParseError<'_> { + self.new_basic_error(BasicParseErrorKind::UnexpectedToken(token)) + } + + /// Create a new BasicParseError at this location + #[inline] + pub fn new_basic_error(self, kind: BasicParseErrorKind<'_>) -> BasicParseError<'_> { BasicParseError { - kind: BasicParseErrorKind::UnexpectedToken(token), + kind, location: self, } } @@ -126,8 +132,14 @@ impl SourceLocation { /// Create a new ParseError at this location for an unexpected token #[inline] pub fn new_unexpected_token_error(self, token: Token<'_>) -> ParseError<'_, E> { + self.new_error(BasicParseErrorKind::UnexpectedToken(token)) + } + + /// Create a new basic ParseError at the current location + #[inline] + pub fn new_error(self, kind: BasicParseErrorKind<'_>) -> ParseError<'_, E> { ParseError { - kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(token)), + kind: ParseErrorKind::Basic(kind), location: self, } } @@ -450,19 +462,13 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Create a new BasicParseError at the current location #[inline] pub fn new_basic_error(&self, kind: BasicParseErrorKind<'i>) -> BasicParseError<'i> { - BasicParseError { - kind, - location: self.current_source_location(), - } + self.current_source_location().new_basic_error(kind) } /// Create a new basic ParseError at the current location #[inline] pub fn new_error(&self, kind: BasicParseErrorKind<'i>) -> ParseError<'i, E> { - ParseError { - kind: ParseErrorKind::Basic(kind), - location: self.current_source_location(), - } + self.current_source_location().new_error(kind) } /// Create a new custom BasicParseError at the current location diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index 9240f6de..48da02b5 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -4,9 +4,7 @@ // https://drafts.csswg.org/css-syntax/#parsing -use super::{ - BasicParseError, BasicParseErrorKind, Delimiter, Delimiters, ParseError, Parser, Token, -}; +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}; @@ -293,7 +291,7 @@ where &start, self.input, &mut *self.parser, - Delimiter::Semicolon | Delimiter::CurlyBracketBlock, + /* nested = */ true, ) { return Some(Ok(qual)); } @@ -304,12 +302,8 @@ where token => { let result = if self.parser.parse_qualified() { self.input.reset(&start); - let delimiters = if self.parser.parse_declarations() { - Delimiter::Semicolon | Delimiter::CurlyBracketBlock - } else { - Delimiter::CurlyBracketBlock - }; - parse_qualified_rule(&start, self.input, &mut *self.parser, delimiters) + 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, |_| { @@ -398,7 +392,7 @@ where &start, self.input, &mut *self.parser, - Delimiter::CurlyBracketBlock, + /* nested = */ false, ); return Some(result.map_err(|e| (e, self.input.slice_from(start.position())))); } @@ -451,7 +445,7 @@ where 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, Delimiter::CurlyBracketBlock) + parse_qualified_rule(&start, input, parser, /* nested = */ false) } }) } @@ -491,16 +485,52 @@ where } } +// If the first two non- values of rule’s prelude are an whose +// value starts with "--" followed by a , then... +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() +} + +// https://drafts.csswg.org/css-syntax/#consume-a-qualified-rule fn parse_qualified_rule<'i, 't, P, E>( start: &ParserState, input: &mut Parser<'i, 't>, parser: &mut P, - delimiters: Delimiters, + nested: bool, ) -> Result<

>::QualifiedRule, ParseError<'i, E>> where P: QualifiedRuleParser<'i, Error = E>, { - let prelude = input.parse_until_before(delimiters, |input| parser.parse_prelude(input)); + input.skip_whitespace(); + let prelude = { + let state = input.state(); + if looks_like_a_custom_property(input) { + // If nested is true, consume the remnants of a bad declaration from input, with + // nested set to true, and return nothing. + // If nested is false, consume a block from input, and return nothing. + 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()?; // Do this here so that we consume the `{` even if the prelude is `Err`. let prelude = prelude?; From 4765111c002e247c9c9e2cccefd239ddd5c46b38 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 5 Jun 2024 23:56:56 +0100 Subject: [PATCH 13/21] [unicode-range] Avoid integer overflow (panic in debug builds) If a long string of hex digits are passed to unicode-range, the code tries to parse them into a number before checking whether there are more than the 6 digits allowed by the syntax, and this may lead to integer overflow. To avoid this, check the number of digits and error out earlier if there are too many to possibly be valid. (See https://bugzilla.mozilla.org/show_bug.cgi?id=1900403) --- src/css-parsing-tests/urange.json | 5 +++++ src/unicode_range.rs | 14 +++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/css-parsing-tests/urange.json b/src/css-parsing-tests/urange.json index 857d1d62..4dcb529c 100644 --- a/src/css-parsing-tests/urange.json +++ b/src/css-parsing-tests/urange.json @@ -84,6 +84,11 @@ null, null, null +], + +"U+26F9200D2640, U+10000-26F9200D2640", [ + null, + null ] ] diff --git a/src/unicode_range.rs b/src/unicode_range.rs index ce6bb3b5..a4130ef0 100644 --- a/src/unicode_range.rs +++ b/src/unicode_range.rs @@ -104,7 +104,7 @@ fn parse_concatenated(text: &[u8]) -> Result { Some((&b'+', text)) => text, _ => return Err(()), }; - let (first_hex_value, hex_digit_count) = consume_hex(&mut text); + let (first_hex_value, hex_digit_count) = consume_hex(&mut text, 6)?; let question_marks = consume_question_marks(&mut text); let consumed = hex_digit_count + question_marks; if consumed == 0 || consumed > 6 { @@ -124,7 +124,7 @@ fn parse_concatenated(text: &[u8]) -> Result { end: first_hex_value, }); } else if let Some((&b'-', mut text)) = text.split_first() { - let (second_hex_value, hex_digit_count) = consume_hex(&mut text); + let (second_hex_value, hex_digit_count) = consume_hex(&mut text, 6)?; if hex_digit_count > 0 && hex_digit_count <= 6 && text.is_empty() { return Ok(UnicodeRange { start: first_hex_value, @@ -135,19 +135,23 @@ fn parse_concatenated(text: &[u8]) -> Result { Err(()) } -fn consume_hex(text: &mut &[u8]) -> (u32, usize) { +// Consume hex digits, but return an error if more than digit_limit are found. +fn consume_hex(text: &mut &[u8], digit_limit: usize) -> Result<(u32, usize), ()> { let mut value = 0; let mut digits = 0; while let Some((&byte, rest)) = text.split_first() { if let Some(digit_value) = (byte as char).to_digit(16) { + if digits == digit_limit { + return Err(()); + } value = value * 0x10 + digit_value; digits += 1; - *text = rest + *text = rest; } else { break; } } - (value, digits) + Ok((value, digits)) } fn consume_question_marks(text: &mut &[u8]) -> usize { From bb071313db0d23a1cea87d1589e42e4ff388e51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 7 Nov 2024 18:25:09 +0100 Subject: [PATCH 14/21] Fix clippy lints. --- src/cow_rc_str.rs | 28 ++++++++++++++-------------- src/parser.rs | 8 ++++---- src/rules_and_declarations.rs | 4 ++-- src/serializer.rs | 6 +++--- src/tests.rs | 4 ++-- src/tokenizer.rs | 14 +++++--------- 6 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/cow_rc_str.rs b/src/cow_rc_str.rs index 26508481..03631f47 100644 --- a/src/cow_rc_str.rs +++ b/src/cow_rc_str.rs @@ -51,7 +51,7 @@ impl<'a> From<&'a str> for CowRcStr<'a> { } } -impl<'a> From for CowRcStr<'a> { +impl From for CowRcStr<'_> { #[inline] fn from(s: String) -> Self { CowRcStr::from_rc(Rc::new(s)) @@ -84,7 +84,7 @@ impl<'a> CowRcStr<'a> { } } -impl<'a> Clone for CowRcStr<'a> { +impl Clone for CowRcStr<'_> { #[inline] fn clone(&self) -> Self { match self.unpack() { @@ -99,7 +99,7 @@ impl<'a> Clone for CowRcStr<'a> { } } -impl<'a> Drop for CowRcStr<'a> { +impl Drop for CowRcStr<'_> { #[inline] fn drop(&mut self) { if let Err(ptr) = self.unpack() { @@ -108,7 +108,7 @@ impl<'a> Drop for CowRcStr<'a> { } } -impl<'a> ops::Deref for CowRcStr<'a> { +impl ops::Deref for CowRcStr<'_> { type Target = str; #[inline] @@ -119,65 +119,65 @@ impl<'a> ops::Deref for CowRcStr<'a> { // Boilerplate / trivial impls below. -impl<'a> AsRef for CowRcStr<'a> { +impl AsRef for CowRcStr<'_> { #[inline] fn as_ref(&self) -> &str { self } } -impl<'a> Borrow for CowRcStr<'a> { +impl Borrow for CowRcStr<'_> { #[inline] fn borrow(&self) -> &str { self } } -impl<'a> Default for CowRcStr<'a> { +impl Default for CowRcStr<'_> { #[inline] fn default() -> Self { Self::from("") } } -impl<'a> hash::Hash for CowRcStr<'a> { +impl hash::Hash for CowRcStr<'_> { #[inline] fn hash(&self, hasher: &mut H) { str::hash(self, hasher) } } -impl<'a, T: AsRef> PartialEq for CowRcStr<'a> { +impl> PartialEq for CowRcStr<'_> { #[inline] fn eq(&self, other: &T) -> bool { str::eq(self, other.as_ref()) } } -impl<'a, T: AsRef> PartialOrd for CowRcStr<'a> { +impl> PartialOrd for CowRcStr<'_> { #[inline] fn partial_cmp(&self, other: &T) -> Option { str::partial_cmp(self, other.as_ref()) } } -impl<'a> Eq for CowRcStr<'a> {} +impl Eq for CowRcStr<'_> {} -impl<'a> Ord for CowRcStr<'a> { +impl Ord for CowRcStr<'_> { #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { str::cmp(self, other) } } -impl<'a> fmt::Display for CowRcStr<'a> { +impl fmt::Display for CowRcStr<'_> { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { str::fmt(self, formatter) } } -impl<'a> fmt::Debug for CowRcStr<'a> { +impl fmt::Debug for CowRcStr<'_> { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { str::fmt(self, formatter) diff --git a/src/parser.rs b/src/parser.rs index dd35fc50..0a432912 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -76,7 +76,7 @@ pub enum BasicParseErrorKind<'i> { QualifiedRuleInvalid, } -impl<'i> fmt::Display for BasicParseErrorKind<'i> { +impl fmt::Display for BasicParseErrorKind<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { BasicParseErrorKind::UnexpectedToken(token) => { @@ -176,7 +176,7 @@ impl<'i, T> ParseErrorKind<'i, T> { } } -impl<'i, E: fmt::Display> fmt::Display for ParseErrorKind<'i, E> { +impl fmt::Display for ParseErrorKind<'_, E> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { ParseErrorKind::Basic(ref basic) => basic.fmt(f), @@ -218,13 +218,13 @@ impl<'i, T> ParseError<'i, T> { } } -impl<'i, E: fmt::Display> fmt::Display for ParseError<'i, E> { +impl fmt::Display for ParseError<'_, E> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.kind.fmt(f) } } -impl<'i, E: fmt::Display + fmt::Debug> std::error::Error for ParseError<'i, E> {} +impl std::error::Error for ParseError<'_, E> {} /// The owned input for a parser. pub struct ParserInput<'i> { diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index 48da02b5..188e354e 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -241,7 +241,7 @@ impl<'i, 't, 'a, P, I, E> RuleBodyParser<'i, 't, 'a, P, I, E> { } /// https://drafts.csswg.org/css-syntax/#consume-a-blocks-contents -impl<'i, 't, 'a, I, P, E: 'i> Iterator for RuleBodyParser<'i, 't, 'a, P, I, E> +impl<'i, I, P, E: 'i> Iterator for RuleBodyParser<'i, '_, '_, P, I, E> where P: RuleBodyItemParser<'i, I, E>, { @@ -349,7 +349,7 @@ where } /// `RuleListParser` is an iterator that yields `Ok(_)` for a rule or an `Err(..)` for an invalid one. -impl<'i, 't, 'a, R, P, E: 'i> Iterator for StyleSheetParser<'i, 't, 'a, P> +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>, diff --git a/src/serializer.rs b/src/serializer.rs index 3c6e31cb..5df73954 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -55,7 +55,7 @@ where Ok(()) } -impl<'a> ToCss for Token<'a> { +impl ToCss for Token<'_> { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write, @@ -311,7 +311,7 @@ where } } -impl<'a, W> fmt::Write for CssStringWriter<'a, W> +impl fmt::Write for CssStringWriter<'_, W> where W: fmt::Write, { @@ -539,7 +539,7 @@ impl TokenSerializationType { } } -impl<'a> Token<'a> { +impl Token<'_> { /// Categorize a token into a type that determines when `/**/` needs to be inserted /// between two tokens when serialized next to each other without whitespace in between. /// diff --git a/src/tests.rs b/src/tests.rs index 7389664d..ec0fc517 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -773,7 +773,7 @@ where } } -impl<'a> ToJson for CowRcStr<'a> { +impl ToJson for CowRcStr<'_> { fn to_json(&self) -> Value { let s: &str = self; s.to_json() @@ -946,7 +946,7 @@ impl<'i> QualifiedRuleParser<'i> for JsonParser { } } -impl<'i> RuleBodyItemParser<'i, Value, ()> for JsonParser { +impl RuleBodyItemParser<'_, Value, ()> for JsonParser { fn parse_qualified(&self) -> bool { true } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index ea173a5e..2e86c66a 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -190,7 +190,7 @@ pub enum Token<'a> { CloseCurlyBracket, } -impl<'a> Token<'a> { +impl Token<'_> { /// Return whether this token represents a parse error. /// /// `BadUrl` and `BadString` are tokenizer-level parse errors. @@ -324,11 +324,11 @@ impl<'a> Tokenizer<'a> { let current = self.position(); let start = self .slice(SourcePosition(0)..current) - .rfind(|c| matches!(c, '\r' | '\n' | '\x0C')) + .rfind(['\r', '\n', '\x0C']) .map_or(0, |start| start + 1); let end = self .slice(current..SourcePosition(self.input.len())) - .find(|c| matches!(c, '\r' | '\n' | '\x0C')) + .find(['\r', '\n', '\x0C']) .map_or(self.input.len(), |end| current.0 + end); self.slice(SourcePosition(start)..SourcePosition(end)) } @@ -720,9 +720,7 @@ fn check_for_source_map<'a>(tokenizer: &mut Tokenizer<'a>, contents: &'a str) { // If there is a source map directive, extract the URL. if contents.starts_with(directive) || contents.starts_with(directive_old) { let contents = &contents[directive.len()..]; - tokenizer.source_map_url = contents - .split(|c| c == ' ' || c == '\t' || c == '\x0C' || c == '\r' || c == '\n') - .next() + tokenizer.source_map_url = contents.split([' ', '\t', '\x0C', '\r', '\n']).next(); } let directive = "# sourceURL="; @@ -731,9 +729,7 @@ fn check_for_source_map<'a>(tokenizer: &mut Tokenizer<'a>, contents: &'a str) { // If there is a source map directive, extract the URL. if contents.starts_with(directive) || contents.starts_with(directive_old) { let contents = &contents[directive.len()..]; - tokenizer.source_url = contents - .split(|c| c == ' ' || c == '\t' || c == '\x0C' || c == '\r' || c == '\n') - .next() + tokenizer.source_url = contents.split([' ', '\t', '\x0C', '\r', '\n']).next() } } From 6fb149bc852a019b08958eac199720404c6b4932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 7 Nov 2024 19:17:56 +0100 Subject: [PATCH 15/21] Bump cssparser_color version. (#395) --- color/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/color/Cargo.toml b/color/Cargo.toml index 47544815..d5cde913 100644 --- a/color/Cargo.toml +++ b/color/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser-color" -version = "0.1.0" +version = "0.2.0" authors = ["Emilio Cobos Álvarez "] description = "Color implementation based on cssparser" documentation = "https://docs.rs/cssparser-color/" @@ -12,7 +12,7 @@ edition = "2021" path = "lib.rs" [dependencies] -cssparser = { path = ".." } +cssparser = { path = "..", version = "0.34" } serde = { version = "1.0", features = ["derive"], optional = true } [features] From fa6f5eb23f058c6fce444ac781b0b380003fdb59 Mon Sep 17 00:00:00 2001 From: AlaskanEmily <31194934+AlaskanEmily@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:07:38 -0800 Subject: [PATCH 16/21] Minor optimization of Delimiters::from_byte to avoid an extra branch. (#397) Co-authored-by: AlaskanEmily --- src/parser.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 0a432912..aabeea6a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -360,10 +360,8 @@ impl Delimiters { table }; - match byte { - None => Delimiter::None, - Some(b) => TABLE[b as usize], - } + assert_eq!(TABLE[0], Delimiter::None); + TABLE[byte.unwrap_or(0) as usize] } } From 008a91c3cea7da09baf092c9be8b77c4d385141a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 21 Feb 2025 11:30:43 +0100 Subject: [PATCH 17/21] Fix clippy lints (#401) --- src/from_bytes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/from_bytes.rs b/src/from_bytes.rs index 78a56d3e..7d9d2c76 100644 --- a/src/from_bytes.rs +++ b/src/from_bytes.rs @@ -24,9 +24,9 @@ pub trait EncodingSupport { /// /// * `css_bytes`: A byte string. /// * `protocol_encoding`: The encoding label, if any, defined by HTTP or equivalent protocol. -/// (e.g. via the `charset` parameter of the `Content-Type` header.) +/// (e.g. via the `charset` parameter of the `Content-Type` header.) /// * `environment_encoding`: An optional `Encoding` object for the [environment encoding] -/// (https://drafts.csswg.org/css-syntax/#environment-encoding), if any. +/// (https://drafts.csswg.org/css-syntax/#environment-encoding), if any. /// /// Returns the encoding to use. pub fn stylesheet_encoding( From 5e477ab8195f64ed9b22ab1cf7b08685347f3879 Mon Sep 17 00:00:00 2001 From: Nicolas Chevobbe Date: Fri, 21 Feb 2025 11:30:02 +0100 Subject: [PATCH 18/21] Add declaration_start parameter to DeclarationParser parse_value (#400) --- src/rules_and_declarations.rs | 6 ++++-- src/tests.rs | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rules_and_declarations.rs b/src/rules_and_declarations.rs index 188e354e..bdaef077 100644 --- a/src/rules_and_declarations.rs +++ b/src/rules_and_declarations.rs @@ -49,6 +49,7 @@ pub trait DeclarationParser<'i> { &mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>, + _declaration_start: &ParserState, ) -> Result> { Err(input.new_error(BasicParseErrorKind::UnexpectedToken(Token::Ident(name)))) } @@ -279,7 +280,7 @@ where error_behavior, |input| { input.expect_colon()?; - parser.parse_value(name, input) + parser.parse_value(name, input, &start) }, ) }; @@ -408,12 +409,13 @@ pub fn parse_one_declaration<'i, 't, P, E>( 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) + parser.parse_value(name, input, &start) }) .map_err(|e| (e, input.slice_from(start_position))) } diff --git a/src/tests.rs b/src/tests.rs index ec0fc517..3c122f0d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -849,6 +849,7 @@ impl<'i> DeclarationParser<'i> for JsonParser { &mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>, + _declaration_start: &ParserState, ) -> Result> { let mut value = vec![]; let mut important = false; From 0d800216303aa6586b1469cb948859fb1dbd6a2f Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Fri, 21 Feb 2025 23:31:11 +1300 Subject: [PATCH 19/21] Implement `MallocSizeOf` for cssparser types (#399) * Implement MallocSizeOf for cssparser types * Fix clippy lints Signed-off-by: Nico Burns --------- Signed-off-by: Nico Burns --- .github/workflows/main.yml | 1 + Cargo.toml | 3 ++- src/serializer.rs | 3 +++ src/tokenizer.rs | 6 ++++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b08ddef7..a7be6aec 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,6 +22,7 @@ jobs: features: - - --features dummy_match_byte + - --features malloc_size_of include: - toolchain: nightly features: --features bench diff --git a/Cargo.toml b/Cargo.toml index f783f19f..f4e980fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser" -version = "0.34.0" +version = "0.34.1" authors = ["Simon Sapin "] description = "Rust implementation of CSS Syntax Level 3" @@ -25,6 +25,7 @@ dtoa-short = "0.3" itoa = "1.0" phf = { version = "0.11.2", features = ["macros"] } serde = { version = "1.0", features = ["derive"], optional = true } +malloc_size_of = { version = "0.1", default-features = false, optional = true } smallvec = "1.0" [profile.profiling] diff --git a/src/serializer.rs b/src/serializer.rs index 5df73954..6696a622 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -466,6 +466,9 @@ pub enum TokenSerializationType { Other, } +#[cfg(feature = "malloc_size_of")] +malloc_size_of::malloc_size_of_is_0!(TokenSerializationType); + impl TokenSerializationType { /// Return a value that represents the absence of a token, e.g. before the start of the input. #[deprecated( diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 2e86c66a..892d10ae 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -533,6 +533,9 @@ impl<'a> Tokenizer<'a> { #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] pub struct SourcePosition(pub(crate) usize); +#[cfg(feature = "malloc_size_of")] +malloc_size_of::malloc_size_of_is_0!(SourcePosition); + impl SourcePosition { /// Returns the current byte index in the original input. #[inline] @@ -552,6 +555,9 @@ pub struct SourceLocation { pub column: u32, } +#[cfg(feature = "malloc_size_of")] +malloc_size_of::malloc_size_of_is_0!(SourceLocation); + fn next_token<'a>(tokenizer: &mut Tokenizer<'a>) -> Result, ()> { if tokenizer.is_eof() { return Err(()); From 958a3f098acb92ddacdce18a7ef2c4a87ac3326f Mon Sep 17 00:00:00 2001 From: Nicolas Chevobbe Date: Fri, 21 Feb 2025 14:28:55 +0100 Subject: [PATCH 20/21] Derive Default for SourceLocation (#402) --- src/tokenizer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 892d10ae..1c85e10a 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -545,7 +545,7 @@ impl SourcePosition { } /// The line and column number for a given position within the input. -#[derive(PartialEq, Eq, Debug, Clone, Copy)] +#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)] pub struct SourceLocation { /// The line number, starting at 0 for the first line. pub line: u32, From 3dd4f63639925438d9932dafc0d81395847f9be4 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Fri, 21 Mar 2025 07:11:19 +1300 Subject: [PATCH 21/21] Bump version 0.35.0 (#404) Signed-off-by: Nico Burns --- Cargo.toml | 2 +- color/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f4e980fd..6604f20e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser" -version = "0.34.1" +version = "0.35.0" authors = ["Simon Sapin "] description = "Rust implementation of CSS Syntax Level 3" diff --git a/color/Cargo.toml b/color/Cargo.toml index d5cde913..5dd6aac7 100644 --- a/color/Cargo.toml +++ b/color/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cssparser-color" -version = "0.2.0" +version = "0.3.0" authors = ["Emilio Cobos Álvarez "] description = "Color implementation based on cssparser" documentation = "https://docs.rs/cssparser-color/" @@ -12,7 +12,7 @@ edition = "2021" path = "lib.rs" [dependencies] -cssparser = { path = "..", version = "0.34" } +cssparser = { path = "..", version = "0.35" } serde = { version = "1.0", features = ["derive"], optional = true } [features]