diff --git a/Cargo.lock b/Cargo.lock index 74f01af6..e2b0b617 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,14 +16,14 @@ checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" [[package]] name = "cssparser" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9cdaae01d5ed7882b04d795e7f752f46ff52d2fa3b50a20d28c464510bba98" +checksum = "11119743ad110e8c1bdccd930d7f5c30c99e5fc76a7b63ec9807e84eef0c5f59" dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.13.1", + "phf", "smallvec", ] @@ -135,9 +135,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "libc" -version = "0.2.189" +version = "0.2.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" [[package]] name = "lock_api" @@ -200,55 +200,25 @@ dependencies = [ "windows-link", ] -[[package]] -name = "phf" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" -dependencies = [ - "phf_macros", - "phf_shared 0.13.1", - "serde", -] - [[package]] name = "phf" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6" dependencies = [ - "phf_shared 0.14.0", + "phf_macros", + "phf_shared", "serde", ] -[[package]] -name = "phf_codegen" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" -dependencies = [ - "phf_generator 0.13.1", - "phf_shared 0.13.1", -] - [[package]] name = "phf_codegen" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41b585a510fb76fdebead6897982ef2a03a21d8e6cbcca904999742a4afc6ffe" dependencies = [ - "phf_generator 0.14.0", - "phf_shared 0.14.0", -] - -[[package]] -name = "phf_generator" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" -dependencies = [ - "fastrand", - "phf_shared 0.13.1", + "phf_generator", + "phf_shared", ] [[package]] @@ -258,31 +228,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aeb62e0959d5a1bebc965f4d15d9e2b7cea002b6b0f5ba8cde6cc26738467100" dependencies = [ "fastrand", - "phf_shared 0.14.0", + "phf_shared", ] [[package]] name = "phf_macros" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +checksum = "5fa8d0ca26d424d27630da600c6624696e7dec8bf7b3b492b383c5dc49e5e085" dependencies = [ - "phf_generator 0.13.1", - "phf_shared 0.13.1", + "phf_generator", + "phf_shared", "proc-macro2", "quote", "syn 2.0.119", ] -[[package]] -name = "phf_shared" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" -dependencies = [ - "siphasher", -] - [[package]] name = "phf_shared" version = "0.14.0" @@ -348,7 +309,7 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scraper" -version = "0.27.0" +version = "0.28.0" dependencies = [ "cssparser", "ego-tree", @@ -362,17 +323,15 @@ dependencies = [ [[package]] name = "selectors" -version = "0.40.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeee001a77567bb05e71652718d3ff76d8697c151ac523e68e9513194eb2b75a" +checksum = "8322aebea94a6cc288ff884e8a49bdbceef60206c400c6c46d22e62fc4fd91a6" dependencies = [ "bitflags", "cssparser", "derive_more", "log", "new_debug_unreachable", - "phf 0.13.1", - "phf_codegen 0.13.1", "precomputed-hash", "rustc-hash", "servo_arc", @@ -416,24 +375,24 @@ dependencies = [ [[package]] name = "servo_arc" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +checksum = "b4eceb519ec34d8290aaf6e46f435102f2bfa63f22b05a144fb16ab266d3b198" dependencies = [ "stable_deref_trait", ] [[package]] name = "siphasher" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" +checksum = "33f4fe9184a62d842c9ef383018f3306d8ba224fd9d836f56d7288308847c256" [[package]] name = "smallvec" -version = "1.16.1" +version = "1.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" +checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" [[package]] name = "stable_deref_trait" @@ -449,7 +408,7 @@ checksum = "ffa8a5dbe8b3f0bbe29d4c3225daafaeead63afdc1b65fc4c01a1384166038e6" dependencies = [ "new_debug_unreachable", "parking_lot", - "phf_shared 0.14.0", + "phf_shared", "precomputed-hash", ] @@ -459,8 +418,8 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "928dcdf75e47626b3617a976ec205d9f057584c371c1f23b782129268d0e6edc" dependencies = [ - "phf_generator 0.14.0", - "phf_shared 0.14.0", + "phf_generator", + "phf_shared", "proc-macro2", "quote", ] @@ -514,8 +473,8 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7572660c8890448ba236b7376f27e389c6a7e1c70195622faced601f855c0ada" dependencies = [ - "phf 0.14.0", - "phf_codegen 0.14.0", + "phf", + "phf_codegen", "string_cache", "string_cache_codegen", ] diff --git a/scraper/Cargo.toml b/scraper/Cargo.toml index a8d25a10..001f6a65 100644 --- a/scraper/Cargo.toml +++ b/scraper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "scraper" -version = "0.27.0" +version = "0.28.0" edition = "2024" description = "HTML parsing and querying with CSS selectors" @@ -14,12 +14,12 @@ repository = "https://github.com/rust-scraper/scraper" readme = "README.md" [dependencies] -cssparser = "0.37.0" +cssparser = "0.38.0" ego-tree = "0.11.0" html5ever = "0.40.0" indexmap = { version = "2.14.0", optional = true } precomputed-hash = "0.1.1" -selectors = "0.40.0" +selectors = "0.41.0" serde = { version = "1.0.228", optional = true } [dependencies.getopts] diff --git a/scraper/src/error.rs b/scraper/src/error.rs index b9e86f52..9c3f6f55 100644 --- a/scraper/src/error.rs +++ b/scraper/src/error.rs @@ -1,24 +1,22 @@ //! Custom error types for diagnostics //! Includes re-exported error types from dependencies -mod utils; - use std::{error::Error, fmt::Display}; -use cssparser::{BasicParseErrorKind, ParseErrorKind, Token}; +use cssparser::{BasicParseErrorKind, ParseErrorKind}; use selectors::parser::SelectorParseErrorKind; /// Error type that is returned when calling `Selector::parse` #[derive(Debug, Clone)] -pub enum SelectorErrorKind<'a> { +pub enum SelectorErrorKind { /// A `Token` was not expected - UnexpectedToken(Token<'a>), + UnexpectedToken, /// End-Of-Line was unexpected EndOfLine, /// `@` rule is invalid - InvalidAtRule(String), + InvalidAtRule, /// The body of an `@` rule is invalid InvalidAtRuleBody, @@ -26,18 +24,21 @@ pub enum SelectorErrorKind<'a> { /// The qualified rule is invalid QualRuleInvalid, + /// Too many nested blocks + TooManyNestedBlocks, + /// Expected a `::` for a pseudoelement - ExpectedColonOnPseudoElement(Token<'a>), + ExpectedColonOnPseudoElement, /// Expected an identity for a pseudoelement - ExpectedIdentityOnPseudoElement(Token<'a>), + ExpectedIdentityOnPseudoElement, /// A `SelectorParseErrorKind` error that isn't really supposed to happen did - UnexpectedSelectorParseError(SelectorParseErrorKind<'a>), + UnexpectedSelectorParseError(SelectorParseErrorKind), } -impl<'a> From>> for SelectorErrorKind<'a> { - fn from(original: cssparser::ParseError<'a, SelectorParseErrorKind<'a>>) -> Self { +impl From> for SelectorErrorKind { + fn from(original: cssparser::ParseError) -> Self { // NOTE: This could be improved, but I dont // exactly know how match original.kind { @@ -47,71 +48,67 @@ impl<'a> From>> for Selecto } } -impl<'a> From> for SelectorErrorKind<'a> { - fn from(err: BasicParseErrorKind<'a>) -> Self { +impl From for SelectorErrorKind { + fn from(err: BasicParseErrorKind) -> Self { match err { - BasicParseErrorKind::UnexpectedToken(token) => Self::UnexpectedToken(token), + BasicParseErrorKind::UnexpectedToken => Self::UnexpectedToken, BasicParseErrorKind::EndOfInput => Self::EndOfLine, - BasicParseErrorKind::AtRuleInvalid(rule) => Self::InvalidAtRule(rule.to_string()), + BasicParseErrorKind::AtRuleInvalid => Self::InvalidAtRule, BasicParseErrorKind::AtRuleBodyInvalid => Self::InvalidAtRuleBody, BasicParseErrorKind::QualifiedRuleInvalid => Self::QualRuleInvalid, + BasicParseErrorKind::TooManyNestedBlocks => Self::TooManyNestedBlocks, } } } -impl<'a> From> for SelectorErrorKind<'a> { - fn from(err: SelectorParseErrorKind<'a>) -> Self { +impl From for SelectorErrorKind { + fn from(err: SelectorParseErrorKind) -> Self { match err { - SelectorParseErrorKind::PseudoElementExpectedColon(token) => { - Self::ExpectedColonOnPseudoElement(token) + SelectorParseErrorKind::PseudoElementExpectedColon => { + Self::ExpectedColonOnPseudoElement } - SelectorParseErrorKind::PseudoElementExpectedIdent(token) => { - Self::ExpectedIdentityOnPseudoElement(token) + SelectorParseErrorKind::PseudoElementExpectedIdent => { + Self::ExpectedIdentityOnPseudoElement } other => Self::UnexpectedSelectorParseError(other), } } } -impl Display for SelectorErrorKind<'_> { +impl Display for SelectorErrorKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::UnexpectedToken(token) => { - format!("Token {:?} was not expected", utils::render_token(token)) - } - Self::EndOfLine => "Unexpected EOL".to_string(), - Self::InvalidAtRule(rule) => format!("Invalid @-rule {rule:?}"), - Self::InvalidAtRuleBody => "The body of an @-rule was invalid".to_string(), - Self::QualRuleInvalid => "The qualified name was invalid".to_string(), - Self::ExpectedColonOnPseudoElement(token) => format!( - "Expected a ':' token for pseudoelement, got {:?} instead", - utils::render_token(token) - ), - Self::ExpectedIdentityOnPseudoElement(token) => format!( - "Expected identity for pseudoelement, got {:?} instead", - utils::render_token(token) - ), - Self::UnexpectedSelectorParseError(err) => format!( + let msg = match self { + Self::UnexpectedToken => "Token was not expected", + Self::EndOfLine => "Unexpected EOL", + Self::InvalidAtRule => "Invalid @-rule", + Self::InvalidAtRuleBody => "The body of an @-rule was invalid", + Self::QualRuleInvalid => "The qualified name was invalid", + Self::TooManyNestedBlocks => "Too many nested blocks", + Self::ExpectedColonOnPseudoElement => "Missing colon character on pseudoelement", + Self::ExpectedIdentityOnPseudoElement => "Missing pseudoelement identity", + Self::UnexpectedSelectorParseError(err) => { + return write!( + f, "Unexpected error occurred. Please report this to the developer\n{err:#?}" - ), + ); } - ) + }; + + f.write_str(msg) } } -impl Error for SelectorErrorKind<'_> { +impl Error for SelectorErrorKind { fn description(&self) -> &str { match self { - Self::UnexpectedToken(_) => "Token was not expected", + Self::UnexpectedToken => "Token was not expected", Self::EndOfLine => "Unexpected EOL", - Self::InvalidAtRule(_) => "Invalid @-rule", + Self::InvalidAtRule => "Invalid @-rule", Self::InvalidAtRuleBody => "The body of an @-rule was invalid", Self::QualRuleInvalid => "The qualified name was invalid", - Self::ExpectedColonOnPseudoElement(_) => "Missing colon character on pseudoelement", - Self::ExpectedIdentityOnPseudoElement(_) => "Missing pseudoelement identity", + Self::TooManyNestedBlocks => "Too many nested blocks", + Self::ExpectedColonOnPseudoElement => "Missing colon character on pseudoelement", + Self::ExpectedIdentityOnPseudoElement => "Missing pseudoelement identity", Self::UnexpectedSelectorParseError(_) => "Unexpected error", } } diff --git a/scraper/src/error/utils.rs b/scraper/src/error/utils.rs deleted file mode 100644 index ae4b768a..00000000 --- a/scraper/src/error/utils.rs +++ /dev/null @@ -1,91 +0,0 @@ -use cssparser::Token; - -pub(crate) fn render_token(token: &Token<'_>) -> String { - match token { - Token::Ident(ident) => ident.to_string(), - Token::AtKeyword(value) => format!("@{value}"), - Token::Hash(name) | Token::IDHash(name) => format!("#{name}"), - Token::QuotedString(value) => format!("\"{value}\""), - Token::UnquotedUrl(value) => value.to_string(), - Token::Number { - has_sign: signed, - value: num, - int_value: _, - } - | Token::Percentage { - has_sign: signed, - unit_value: num, - int_value: _, - } => render_number(*signed, *num, token), - Token::Dimension { - has_sign: signed, - value: num, - int_value: _, - unit, - } => format!("{}{}", render_int(*signed, *num), unit), - Token::WhiteSpace(_) => String::from(" "), - Token::Comment(comment) => format!("/* {comment} */"), - Token::Function(name) => format!("{name}()"), - Token::BadString(string) => format!(""), - Token::BadUrl(url) => format!(""), - // Single-character token - Token::Colon => ":".into(), - Token::Semicolon => ";".into(), - Token::Comma => ",".into(), - Token::IncludeMatch => "~=".into(), - Token::DashMatch => "|=".into(), - Token::PrefixMatch => "^=".into(), - Token::SuffixMatch => "$=".into(), - Token::SubstringMatch => "*=".into(), - Token::CDO => "".into(), - Token::ParenthesisBlock => "<(".into(), - Token::SquareBracketBlock => "<[".into(), - Token::CurlyBracketBlock => "<{".into(), - Token::CloseParenthesis => "<)".into(), - Token::CloseSquareBracket => "<]".into(), - Token::CloseCurlyBracket => "<}".into(), - Token::Delim(delim) => (*delim).into(), - } -} - -fn render_number(signed: bool, num: f32, token: &Token) -> String { - let num = render_int(signed, num); - - match token { - Token::Number { .. } => num, - Token::Percentage { .. } => format!("{num}%"), - _ => panic!("render_number is not supposed to be called on a non-numerical token"), - } -} - -fn render_int(signed: bool, num: f32) -> String { - if signed { - render_int_signed(num) - } else { - render_int_unsigned(num) - } -} - -fn render_int_signed(num: f32) -> String { - if num > 0.0 { - format!("+{num}") - } else { - format!("-{num}") - } -} - -fn render_int_unsigned(num: f32) -> String { - format!("{num}") -} - -#[cfg(test)] -mod tests { - use crate::Selector; - - #[test] - fn regression_test_issue212() { - let err = Selector::parse("div138293@!#@!!@#").unwrap_err(); - assert_eq!(err.to_string(), "Token \"@\" was not expected"); - } -} diff --git a/scraper/src/selector.rs b/scraper/src/selector.rs index 17348d28..ab3605d0 100644 --- a/scraper/src/selector.rs +++ b/scraper/src/selector.rs @@ -2,6 +2,7 @@ use std::convert::TryFrom; use std::fmt; +use std::str::FromStr; pub use cssparser::ToCss; use html5ever::{LocalName, Namespace}; @@ -28,9 +29,8 @@ pub struct Selector { impl Selector { /// Parses a CSS selector group. - pub fn parse(selectors: &str) -> Result> { - let mut parser_input = cssparser::ParserInput::new(selectors); - let mut parser = cssparser::Parser::new(&mut parser_input); + pub fn parse(selectors: &str) -> Result { + let mut parser = cssparser::Parser::new(selectors); SelectorList::parse(&Parser, &mut parser, ParseRelative::No) .map(|selectors| Self { selectors }) @@ -118,7 +118,7 @@ impl Visitor<'_> for SelectorVisitor { pub struct Parser; impl<'i> parser::Parser<'i> for Parser { type Impl = Simple; - type Error = SelectorParseErrorKind<'i>; + type Error = SelectorParseErrorKind; fn parse_is_and_where(&self) -> bool { true @@ -204,8 +204,6 @@ impl PrecomputedHash for CssLocalName { pub enum NonTSPseudoClass {} impl parser::NonTSPseudoClass for NonTSPseudoClass { - type Impl = Simple; - fn is_active_or_hover(&self) -> bool { false } @@ -228,9 +226,7 @@ impl ToCss for NonTSPseudoClass { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PseudoElement {} -impl parser::PseudoElement for PseudoElement { - type Impl = Simple; -} +impl parser::PseudoElement for PseudoElement {} impl ToCss for PseudoElement { fn to_css(&self, dest: &mut W) -> fmt::Result @@ -241,10 +237,18 @@ impl ToCss for PseudoElement { } } -impl<'i> TryFrom<&'i str> for Selector { - type Error = SelectorErrorKind<'i>; +impl TryFrom<&str> for Selector { + type Error = SelectorErrorKind; + + fn try_from(s: &str) -> Result { + Selector::parse(s) + } +} + +impl FromStr for Selector { + type Err = SelectorErrorKind; - fn try_from(s: &'i str) -> Result { + fn from_str(s: &str) -> Result { Selector::parse(s) } }