diff --git a/src/parser.rs b/src/parser.rs index f04628db..8484eb12 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -639,17 +639,49 @@ impl<'i: 't, 't> Parser<'i, 't> { /// Parse a list of comma-separated values, all with the same syntax. /// /// The given closure is called repeatedly with a "delimited" parser - /// (see the `Parser::parse_until_before` method) - /// so that it can over consume the input past a comma at this block/function nesting level. + /// (see the `Parser::parse_until_before` method) so that it can over + /// consume the input past a comma at this block/function nesting level. /// /// Successful results are accumulated in a vector. /// /// This method returns `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. + /// or if a closure call leaves some input before the next comma or the end + /// of the input. #[inline] pub fn parse_comma_separated( + &mut self, + parse_one: F, + ) -> Result, ParseError<'i, E>> + where + F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, + { + self.parse_comma_separated_internal(parse_one, /* ignore_errors = */ false) + } + + /// Like `parse_comma_separated`, but ignores errors on unknown components, + /// rather than erroring out in the whole list. + /// + /// Caller must deal with the fact that the resulting list might be empty, + /// if there's no valid component on the list. + #[inline] + pub fn parse_comma_separated_ignoring_errors( + &mut self, + parse_one: F, + ) -> Vec + where + F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, + { + match self.parse_comma_separated_internal(parse_one, /* ignore_errors = */ true) { + Ok(values) => values, + Err(..) => unreachable!(), + } + } + + #[inline] + fn parse_comma_separated_internal( &mut self, mut parse_one: F, + ignore_errors: bool, ) -> Result, ParseError<'i, E>> where F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result>, @@ -661,7 +693,11 @@ impl<'i: 't, 't> Parser<'i, 't> { let mut values = Vec::with_capacity(1); loop { self.skip_whitespace(); // Unnecessary for correctness, but may help try() in parse_one rewind less. - values.push(self.parse_until_before(Delimiter::Comma, &mut parse_one)?); + match self.parse_until_before(Delimiter::Comma, &mut parse_one) { + Ok(v) => values.push(v), + Err(e) if !ignore_errors => return Err(e), + Err(_) => {}, + } match self.next() { Err(_) => return Ok(values), Ok(&Token::Comma) => continue, diff --git a/src/tests.rs b/src/tests.rs index 3547b268..b4d25626 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -418,6 +418,20 @@ fn nth() { }); } +#[test] +fn parse_comma_separated_ignoring_errors() { + let input = "red, green something, yellow, whatever, blue"; + let mut input = ParserInput::new(input); + let mut input = Parser::new(&mut input); + let result = input.parse_comma_separated_ignoring_errors(|input| { + Color::parse(input).map_err(Into::>::into) + }); + assert_eq!(result.len(), 3); + assert_eq!(result[0].to_css_string(), "rgb(255, 0, 0)"); + assert_eq!(result[1].to_css_string(), "rgb(255, 255, 0)"); + assert_eq!(result[2].to_css_string(), "rgb(0, 0, 255)"); +} + #[test] fn unicode_range() { run_json_tests(include_str!("css-parsing-tests/urange.json"), |input| {