Coverage Report

Created: 2026-08-14 08:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/cssparser-0.35.0/src/parser.rs
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
use crate::cow_rc_str::CowRcStr;
6
use crate::tokenizer::{SourceLocation, SourcePosition, Token, Tokenizer};
7
use smallvec::SmallVec;
8
use std::fmt;
9
use std::ops::BitOr;
10
use std::ops::Range;
11
12
/// A capture of the internal state of a `Parser` (including the position within the input),
13
/// obtained from the `Parser::position` method.
14
///
15
/// Can be used with the `Parser::reset` method to restore that state.
16
/// Should only be used with the `Parser` instance it came from.
17
#[derive(Debug, Clone)]
18
pub struct ParserState {
19
    pub(crate) position: usize,
20
    pub(crate) current_line_start_position: usize,
21
    pub(crate) current_line_number: u32,
22
    pub(crate) at_start_of: Option<BlockType>,
23
}
24
25
impl ParserState {
26
    /// The position from the start of the input, counted in UTF-8 bytes.
27
    #[inline]
28
0
    pub fn position(&self) -> SourcePosition {
29
0
        SourcePosition(self.position)
30
0
    }
31
32
    /// The line number and column number
33
    #[inline]
34
0
    pub fn source_location(&self) -> SourceLocation {
35
0
        SourceLocation {
36
0
            line: self.current_line_number,
37
0
            column: (self.position - self.current_line_start_position + 1) as u32,
38
0
        }
39
0
    }
Unexecuted instantiation: <cssparser::parser::ParserState>::source_location
Unexecuted instantiation: <cssparser::parser::ParserState>::source_location
40
}
41
42
/// When parsing until a given token, sometimes the caller knows that parsing is going to restart
43
/// at some earlier point, and consuming until we find a top level delimiter is just wasted work.
44
///
45
/// In that case, callers can pass ParseUntilErrorBehavior::Stop to avoid doing all that wasted
46
/// work.
47
///
48
/// This is important for things like CSS nesting, where something like:
49
///
50
///   foo:is(..) {
51
///     ...
52
///   }
53
///
54
/// Would need to scan the whole {} block to find a semicolon, only for parsing getting restarted
55
/// as a qualified rule later.
56
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
57
pub enum ParseUntilErrorBehavior {
58
    /// Consume until we see the relevant delimiter or the end of the stream.
59
    Consume,
60
    /// Eagerly error.
61
    Stop,
62
}
63
64
/// Details about a `BasicParseError`
65
#[derive(Clone, Debug, PartialEq)]
66
pub enum BasicParseErrorKind<'i> {
67
    /// An unexpected token was encountered.
68
    UnexpectedToken(Token<'i>),
69
    /// The end of the input was encountered unexpectedly.
70
    EndOfInput,
71
    /// An `@` rule was encountered that was invalid.
72
    AtRuleInvalid(CowRcStr<'i>),
73
    /// The body of an '@' rule was invalid.
74
    AtRuleBodyInvalid,
75
    /// A qualified rule was encountered that was invalid.
76
    QualifiedRuleInvalid,
77
}
78
79
impl fmt::Display for BasicParseErrorKind<'_> {
80
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81
0
        match self {
82
0
            BasicParseErrorKind::UnexpectedToken(token) => {
83
0
                write!(f, "unexpected token: {:?}", token)
84
            }
85
0
            BasicParseErrorKind::EndOfInput => write!(f, "unexpected end of input"),
86
0
            BasicParseErrorKind::AtRuleInvalid(rule) => {
87
0
                write!(f, "invalid @ rule encountered: '@{}'", rule)
88
            }
89
0
            BasicParseErrorKind::AtRuleBodyInvalid => write!(f, "invalid @ rule body encountered"),
90
            BasicParseErrorKind::QualifiedRuleInvalid => {
91
0
                write!(f, "invalid qualified rule encountered")
92
            }
93
        }
94
0
    }
95
}
96
97
/// The fundamental parsing errors that can be triggered by built-in parsing routines.
98
#[derive(Clone, Debug, PartialEq)]
99
pub struct BasicParseError<'i> {
100
    /// Details of this error
101
    pub kind: BasicParseErrorKind<'i>,
102
    /// Location where this error occurred
103
    pub location: SourceLocation,
104
}
105
106
impl<'i, T> From<BasicParseError<'i>> for ParseError<'i, T> {
107
    #[inline]
108
0
    fn from(this: BasicParseError<'i>) -> ParseError<'i, T> {
109
0
        ParseError {
110
0
            kind: ParseErrorKind::Basic(this.kind),
111
0
            location: this.location,
112
0
        }
113
0
    }
Unexecuted instantiation: <cssparser::parser::ParseError<()> as core::convert::From<cssparser::parser::BasicParseError>>::from
Unexecuted instantiation: <cssparser::parser::ParseError<_> as core::convert::From<cssparser::parser::BasicParseError>>::from
114
}
115
116
impl SourceLocation {
117
    /// Create a new BasicParseError at this location for an unexpected token
118
    #[inline]
119
0
    pub fn new_basic_unexpected_token_error(self, token: Token<'_>) -> BasicParseError<'_> {
120
0
        self.new_basic_error(BasicParseErrorKind::UnexpectedToken(token))
121
0
    }
Unexecuted instantiation: <cssparser::tokenizer::SourceLocation>::new_basic_unexpected_token_error
Unexecuted instantiation: <cssparser::tokenizer::SourceLocation>::new_basic_unexpected_token_error
122
123
    /// Create a new BasicParseError at this location
124
    #[inline]
125
0
    pub fn new_basic_error(self, kind: BasicParseErrorKind<'_>) -> BasicParseError<'_> {
126
0
        BasicParseError {
127
0
            kind,
128
0
            location: self,
129
0
        }
130
0
    }
Unexecuted instantiation: <cssparser::tokenizer::SourceLocation>::new_basic_error
Unexecuted instantiation: <cssparser::tokenizer::SourceLocation>::new_basic_error
131
132
    /// Create a new ParseError at this location for an unexpected token
133
    #[inline]
134
0
    pub fn new_unexpected_token_error<E>(self, token: Token<'_>) -> ParseError<'_, E> {
135
0
        self.new_error(BasicParseErrorKind::UnexpectedToken(token))
136
0
    }
137
138
    /// Create a new basic ParseError at the current location
139
    #[inline]
140
0
    pub fn new_error<E>(self, kind: BasicParseErrorKind<'_>) -> ParseError<'_, E> {
141
0
        ParseError {
142
0
            kind: ParseErrorKind::Basic(kind),
143
0
            location: self,
144
0
        }
145
0
    }
Unexecuted instantiation: <cssparser::tokenizer::SourceLocation>::new_error::<()>
Unexecuted instantiation: <cssparser::tokenizer::SourceLocation>::new_error::<_>
146
147
    /// Create a new custom ParseError at this location
148
    #[inline]
149
0
    pub fn new_custom_error<'i, E1: Into<E2>, E2>(self, error: E1) -> ParseError<'i, E2> {
150
0
        ParseError {
151
0
            kind: ParseErrorKind::Custom(error.into()),
152
0
            location: self,
153
0
        }
154
0
    }
155
}
156
157
/// Details of a `ParseError`
158
#[derive(Clone, Debug, PartialEq)]
159
pub enum ParseErrorKind<'i, T: 'i> {
160
    /// A fundamental parse error from a built-in parsing routine.
161
    Basic(BasicParseErrorKind<'i>),
162
    /// A parse error reported by downstream consumer code.
163
    Custom(T),
164
}
165
166
impl<'i, T> ParseErrorKind<'i, T> {
167
    /// Like `std::convert::Into::into`
168
0
    pub fn into<U>(self) -> ParseErrorKind<'i, U>
169
0
    where
170
0
        T: Into<U>,
171
    {
172
0
        match self {
173
0
            ParseErrorKind::Basic(basic) => ParseErrorKind::Basic(basic),
174
0
            ParseErrorKind::Custom(custom) => ParseErrorKind::Custom(custom.into()),
175
        }
176
0
    }
177
}
178
179
impl<E: fmt::Display> fmt::Display for ParseErrorKind<'_, E> {
180
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181
0
        match self {
182
0
            ParseErrorKind::Basic(ref basic) => basic.fmt(f),
183
0
            ParseErrorKind::Custom(ref custom) => custom.fmt(f),
184
        }
185
0
    }
186
}
187
188
/// Extensible parse errors that can be encountered by client parsing implementations.
189
#[derive(Clone, Debug, PartialEq)]
190
pub struct ParseError<'i, E> {
191
    /// Details of this error
192
    pub kind: ParseErrorKind<'i, E>,
193
    /// Location where this error occurred
194
    pub location: SourceLocation,
195
}
196
197
impl<'i, T> ParseError<'i, T> {
198
    /// Extract the fundamental parse error from an extensible error.
199
0
    pub fn basic(self) -> BasicParseError<'i> {
200
0
        match self.kind {
201
0
            ParseErrorKind::Basic(kind) => BasicParseError {
202
0
                kind,
203
0
                location: self.location,
204
0
            },
205
0
            ParseErrorKind::Custom(_) => panic!("Not a basic parse error"),
206
        }
207
0
    }
208
209
    /// Like `std::convert::Into::into`
210
0
    pub fn into<U>(self) -> ParseError<'i, U>
211
0
    where
212
0
        T: Into<U>,
213
    {
214
0
        ParseError {
215
0
            kind: self.kind.into(),
216
0
            location: self.location,
217
0
        }
218
0
    }
219
}
220
221
impl<E: fmt::Display> fmt::Display for ParseError<'_, E> {
222
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
223
0
        self.kind.fmt(f)
224
0
    }
225
}
226
227
impl<E: fmt::Display + fmt::Debug> std::error::Error for ParseError<'_, E> {}
228
229
/// The owned input for a parser.
230
pub struct ParserInput<'i> {
231
    tokenizer: Tokenizer<'i>,
232
    cached_token: Option<CachedToken<'i>>,
233
}
234
235
struct CachedToken<'i> {
236
    token: Token<'i>,
237
    start_position: SourcePosition,
238
    end_state: ParserState,
239
}
240
241
impl<'i> ParserInput<'i> {
242
    /// Create a new input for a parser.
243
0
    pub fn new(input: &'i str) -> ParserInput<'i> {
244
0
        ParserInput {
245
0
            tokenizer: Tokenizer::new(input),
246
0
            cached_token: None,
247
0
        }
248
0
    }
249
250
    #[inline]
251
0
    fn cached_token_ref(&self) -> &Token<'i> {
252
0
        &self.cached_token.as_ref().unwrap().token
253
0
    }
254
}
255
256
/// A CSS parser that borrows its `&str` input,
257
/// yields `Token`s,
258
/// and keeps track of nested blocks and functions.
259
pub struct Parser<'i, 't> {
260
    input: &'t mut ParserInput<'i>,
261
    /// If `Some(_)`, .parse_nested_block() can be called.
262
    at_start_of: Option<BlockType>,
263
    /// For parsers from `parse_until` or `parse_nested_block`
264
    stop_before: Delimiters,
265
}
266
267
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
268
pub(crate) enum BlockType {
269
    Parenthesis,
270
    SquareBracket,
271
    CurlyBracket,
272
}
273
274
impl BlockType {
275
0
    fn opening(token: &Token) -> Option<BlockType> {
276
0
        match *token {
277
0
            Token::Function(_) | Token::ParenthesisBlock => Some(BlockType::Parenthesis),
278
0
            Token::SquareBracketBlock => Some(BlockType::SquareBracket),
279
0
            Token::CurlyBracketBlock => Some(BlockType::CurlyBracket),
280
0
            _ => None,
281
        }
282
0
    }
283
284
0
    fn closing(token: &Token) -> Option<BlockType> {
285
0
        match *token {
286
0
            Token::CloseParenthesis => Some(BlockType::Parenthesis),
287
0
            Token::CloseSquareBracket => Some(BlockType::SquareBracket),
288
0
            Token::CloseCurlyBracket => Some(BlockType::CurlyBracket),
289
0
            _ => None,
290
        }
291
0
    }
292
}
293
294
/// A set of characters, to be used with the `Parser::parse_until*` methods.
295
///
296
/// The union of two sets can be obtained with the `|` operator. Example:
297
///
298
/// ```{rust,ignore}
299
/// input.parse_until_before(Delimiter::CurlyBracketBlock | Delimiter::Semicolon)
300
/// ```
301
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
302
pub struct Delimiters {
303
    bits: u8,
304
}
305
306
/// `Delimiters` constants.
307
#[allow(non_upper_case_globals, non_snake_case)]
308
pub mod Delimiter {
309
    use super::Delimiters;
310
311
    /// The empty delimiter set
312
    pub const None: Delimiters = Delimiters { bits: 0 };
313
    /// The delimiter set with only the `{` opening curly bracket
314
    pub const CurlyBracketBlock: Delimiters = Delimiters { bits: 1 << 1 };
315
    /// The delimiter set with only the `;` semicolon
316
    pub const Semicolon: Delimiters = Delimiters { bits: 1 << 2 };
317
    /// The delimiter set with only the `!` exclamation point
318
    pub const Bang: Delimiters = Delimiters { bits: 1 << 3 };
319
    /// The delimiter set with only the `,` comma
320
    pub const Comma: Delimiters = Delimiters { bits: 1 << 4 };
321
}
322
323
#[allow(non_upper_case_globals, non_snake_case)]
324
mod ClosingDelimiter {
325
    use super::Delimiters;
326
327
    pub const CloseCurlyBracket: Delimiters = Delimiters { bits: 1 << 5 };
328
    pub const CloseSquareBracket: Delimiters = Delimiters { bits: 1 << 6 };
329
    pub const CloseParenthesis: Delimiters = Delimiters { bits: 1 << 7 };
330
}
331
332
impl BitOr<Delimiters> for Delimiters {
333
    type Output = Delimiters;
334
335
    #[inline]
336
0
    fn bitor(self, other: Delimiters) -> Delimiters {
337
0
        Delimiters {
338
0
            bits: self.bits | other.bits,
339
0
        }
340
0
    }
341
}
342
343
impl Delimiters {
344
    #[inline]
345
0
    fn contains(self, other: Delimiters) -> bool {
346
0
        (self.bits & other.bits) != 0
347
0
    }
348
349
    #[inline]
350
0
    pub(crate) fn from_byte(byte: Option<u8>) -> Delimiters {
351
        const TABLE: [Delimiters; 256] = {
352
            let mut table = [Delimiter::None; 256];
353
            table[b';' as usize] = Delimiter::Semicolon;
354
            table[b'!' as usize] = Delimiter::Bang;
355
            table[b',' as usize] = Delimiter::Comma;
356
            table[b'{' as usize] = Delimiter::CurlyBracketBlock;
357
            table[b'}' as usize] = ClosingDelimiter::CloseCurlyBracket;
358
            table[b']' as usize] = ClosingDelimiter::CloseSquareBracket;
359
            table[b')' as usize] = ClosingDelimiter::CloseParenthesis;
360
            table
361
        };
362
363
0
        assert_eq!(TABLE[0], Delimiter::None);
364
0
        TABLE[byte.unwrap_or(0) as usize]
365
0
    }
366
}
367
368
/// Used in some `fn expect_*` methods
369
macro_rules! expect {
370
    ($parser: ident, $($branches: tt)+) => {
371
        {
372
            let start_location = $parser.current_source_location();
373
            match *$parser.next()? {
374
                $($branches)+
375
                ref token => {
376
                    return Err(start_location.new_basic_unexpected_token_error(token.clone()))
377
                }
378
            }
379
        }
380
    }
381
}
382
383
impl<'i: 't, 't> Parser<'i, 't> {
384
    /// Create a new parser
385
    #[inline]
386
0
    pub fn new(input: &'t mut ParserInput<'i>) -> Parser<'i, 't> {
387
0
        Parser {
388
0
            input,
389
0
            at_start_of: None,
390
0
            stop_before: Delimiter::None,
391
0
        }
392
0
    }
Unexecuted instantiation: <cssparser::parser::Parser>::new
Unexecuted instantiation: <cssparser::parser::Parser>::new
393
394
    /// Return the current line that is being parsed.
395
0
    pub fn current_line(&self) -> &'i str {
396
0
        self.input.tokenizer.current_source_line()
397
0
    }
398
399
    /// Check whether the input is exhausted. That is, if `.next()` would return a token.
400
    ///
401
    /// This ignores whitespace and comments.
402
    #[inline]
403
0
    pub fn is_exhausted(&mut self) -> bool {
404
0
        self.expect_exhausted().is_ok()
405
0
    }
406
407
    /// Check whether the input is exhausted. That is, if `.next()` would return a token.
408
    /// Return a `Result` so that the `?` operator can be used: `input.expect_exhausted()?`
409
    ///
410
    /// This ignores whitespace and comments.
411
    #[inline]
412
0
    pub fn expect_exhausted(&mut self) -> Result<(), BasicParseError<'i>> {
413
0
        let start = self.state();
414
0
        let result = match self.next() {
415
            Err(BasicParseError {
416
                kind: BasicParseErrorKind::EndOfInput,
417
                ..
418
0
            }) => Ok(()),
419
0
            Err(e) => unreachable!("Unexpected error encountered: {:?}", e),
420
0
            Ok(t) => Err(start
421
0
                .source_location()
422
0
                .new_basic_unexpected_token_error(t.clone())),
423
        };
424
0
        self.reset(&start);
425
0
        result
426
0
    }
Unexecuted instantiation: <cssparser::parser::Parser>::expect_exhausted
Unexecuted instantiation: <cssparser::parser::Parser>::expect_exhausted
427
428
    /// Return the current position within the input.
429
    ///
430
    /// This can be used with the `Parser::slice` and `slice_from` methods.
431
    #[inline]
432
0
    pub fn position(&self) -> SourcePosition {
433
0
        self.input.tokenizer.position()
434
0
    }
435
436
    /// The current line number and column number.
437
    #[inline]
438
0
    pub fn current_source_location(&self) -> SourceLocation {
439
0
        self.input.tokenizer.current_source_location()
440
0
    }
Unexecuted instantiation: <cssparser::parser::Parser>::current_source_location
Unexecuted instantiation: <cssparser::parser::Parser>::current_source_location
441
442
    /// The source map URL, if known.
443
    ///
444
    /// The source map URL is extracted from a specially formatted
445
    /// comment.  The last such comment is used, so this value may
446
    /// change as parsing proceeds.
447
0
    pub fn current_source_map_url(&self) -> Option<&str> {
448
0
        self.input.tokenizer.current_source_map_url()
449
0
    }
450
451
    /// The source URL, if known.
452
    ///
453
    /// The source URL is extracted from a specially formatted
454
    /// comment.  The last such comment is used, so this value may
455
    /// change as parsing proceeds.
456
0
    pub fn current_source_url(&self) -> Option<&str> {
457
0
        self.input.tokenizer.current_source_url()
458
0
    }
459
460
    /// Create a new BasicParseError at the current location
461
    #[inline]
462
0
    pub fn new_basic_error(&self, kind: BasicParseErrorKind<'i>) -> BasicParseError<'i> {
463
0
        self.current_source_location().new_basic_error(kind)
464
0
    }
465
466
    /// Create a new basic ParseError at the current location
467
    #[inline]
468
0
    pub fn new_error<E>(&self, kind: BasicParseErrorKind<'i>) -> ParseError<'i, E> {
469
0
        self.current_source_location().new_error(kind)
470
0
    }
Unexecuted instantiation: <cssparser::parser::Parser>::new_error::<()>
Unexecuted instantiation: <cssparser::parser::Parser>::new_error::<_>
471
472
    /// Create a new custom BasicParseError at the current location
473
    #[inline]
474
0
    pub fn new_custom_error<E1: Into<E2>, E2>(&self, error: E1) -> ParseError<'i, E2> {
475
0
        self.current_source_location().new_custom_error(error)
476
0
    }
477
478
    /// Create a new unexpected token BasicParseError at the current location
479
    #[inline]
480
0
    pub fn new_basic_unexpected_token_error(&self, token: Token<'i>) -> BasicParseError<'i> {
481
0
        self.new_basic_error(BasicParseErrorKind::UnexpectedToken(token))
482
0
    }
483
484
    /// Create a new unexpected token ParseError at the current location
485
    #[inline]
486
0
    pub fn new_unexpected_token_error<E>(&self, token: Token<'i>) -> ParseError<'i, E> {
487
0
        self.new_error(BasicParseErrorKind::UnexpectedToken(token))
488
0
    }
489
490
    /// Create a new unexpected token or EOF ParseError at the current location
491
    #[inline]
492
0
    pub fn new_error_for_next_token<E>(&mut self) -> ParseError<'i, E> {
493
0
        let token = match self.next() {
494
0
            Ok(token) => token.clone(),
495
0
            Err(e) => return e.into(),
496
        };
497
0
        self.new_error(BasicParseErrorKind::UnexpectedToken(token))
498
0
    }
499
500
    /// Return the current internal state of the parser (including position within the input).
501
    ///
502
    /// This state can later be restored with the `Parser::reset` method.
503
    #[inline]
504
0
    pub fn state(&self) -> ParserState {
505
0
        ParserState {
506
0
            at_start_of: self.at_start_of,
507
0
            ..self.input.tokenizer.state()
508
0
        }
509
0
    }
Unexecuted instantiation: <cssparser::parser::Parser>::state
Unexecuted instantiation: <cssparser::parser::Parser>::state
510
511
    /// Advance the input until the next token that’s not whitespace or a comment.
512
    #[inline]
513
0
    pub fn skip_whitespace(&mut self) {
514
0
        if let Some(block_type) = self.at_start_of.take() {
515
0
            consume_until_end_of_block(block_type, &mut self.input.tokenizer);
516
0
        }
517
518
0
        self.input.tokenizer.skip_whitespace()
519
0
    }
520
521
    #[inline]
522
0
    pub(crate) fn skip_cdc_and_cdo(&mut self) {
523
0
        if let Some(block_type) = self.at_start_of.take() {
524
0
            consume_until_end_of_block(block_type, &mut self.input.tokenizer);
525
0
        }
526
527
0
        self.input.tokenizer.skip_cdc_and_cdo()
528
0
    }
529
530
    #[inline]
531
0
    pub(crate) fn next_byte(&self) -> Option<u8> {
532
0
        let byte = self.input.tokenizer.next_byte();
533
0
        if self.stop_before.contains(Delimiters::from_byte(byte)) {
534
0
            return None;
535
0
        }
536
0
        byte
537
0
    }
538
539
    /// Restore the internal state of the parser (including position within the input)
540
    /// to what was previously saved by the `Parser::position` method.
541
    ///
542
    /// Should only be used with `SourcePosition` values from the same `Parser` instance.
543
    #[inline]
544
0
    pub fn reset(&mut self, state: &ParserState) {
545
0
        self.input.tokenizer.reset(state);
546
0
        self.at_start_of = state.at_start_of;
547
0
    }
Unexecuted instantiation: <cssparser::parser::Parser>::reset
Unexecuted instantiation: <cssparser::parser::Parser>::reset
548
549
    /// Start looking for `var()` / `env()` functions. (See the
550
    /// `.seen_var_or_env_functions()` method.)
551
    #[inline]
552
0
    pub fn look_for_var_or_env_functions(&mut self) {
553
0
        self.input.tokenizer.look_for_var_or_env_functions()
554
0
    }
555
556
    /// Return whether a `var()` or `env()` function has been seen by the
557
    /// tokenizer since either `look_for_var_or_env_functions` was called, and
558
    /// stop looking.
559
    #[inline]
560
0
    pub fn seen_var_or_env_functions(&mut self) -> bool {
561
0
        self.input.tokenizer.seen_var_or_env_functions()
562
0
    }
563
564
    /// The old name of `try_parse`, which requires raw identifiers in the Rust 2018 edition.
565
    #[inline]
566
0
    pub fn r#try<F, T, E>(&mut self, thing: F) -> Result<T, E>
567
0
    where
568
0
        F: FnOnce(&mut Parser<'i, 't>) -> Result<T, E>,
569
    {
570
0
        self.try_parse(thing)
571
0
    }
572
573
    /// Execute the given closure, passing it the parser.
574
    /// If the result (returned unchanged) is `Err`,
575
    /// the internal state of the parser  (including position within the input)
576
    /// is restored to what it was before the call.
577
    #[inline]
578
0
    pub fn try_parse<F, T, E>(&mut self, thing: F) -> Result<T, E>
579
0
    where
580
0
        F: FnOnce(&mut Parser<'i, 't>) -> Result<T, E>,
581
    {
582
0
        let start = self.state();
583
0
        let result = thing(self);
584
0
        if result.is_err() {
585
0
            self.reset(&start)
586
0
        }
587
0
        result
588
0
    }
589
590
    /// Return a slice of the CSS input
591
    #[inline]
592
0
    pub fn slice(&self, range: Range<SourcePosition>) -> &'i str {
593
0
        self.input.tokenizer.slice(range)
594
0
    }
595
596
    /// Return a slice of the CSS input, from the given position to the current one.
597
    #[inline]
598
0
    pub fn slice_from(&self, start_position: SourcePosition) -> &'i str {
599
0
        self.input.tokenizer.slice_from(start_position)
600
0
    }
601
602
    /// Return the next token in the input that is neither whitespace or a comment,
603
    /// and advance the position accordingly.
604
    ///
605
    /// After returning a `Function`, `ParenthesisBlock`,
606
    /// `CurlyBracketBlock`, or `SquareBracketBlock` token,
607
    /// the next call will skip until after the matching `CloseParenthesis`,
608
    /// `CloseCurlyBracket`, or `CloseSquareBracket` token.
609
    ///
610
    /// See the `Parser::parse_nested_block` method to parse the content of functions or blocks.
611
    ///
612
    /// This only returns a closing token when it is unmatched (and therefore an error).
613
    #[allow(clippy::should_implement_trait)]
614
0
    pub fn next(&mut self) -> Result<&Token<'i>, BasicParseError<'i>> {
615
0
        self.skip_whitespace();
616
0
        self.next_including_whitespace_and_comments()
617
0
    }
618
619
    /// Same as `Parser::next`, but does not skip whitespace tokens.
620
0
    pub fn next_including_whitespace(&mut self) -> Result<&Token<'i>, BasicParseError<'i>> {
621
        loop {
622
0
            match self.next_including_whitespace_and_comments() {
623
0
                Err(e) => return Err(e),
624
0
                Ok(&Token::Comment(_)) => {}
625
0
                _ => break,
626
            }
627
        }
628
0
        Ok(self.input.cached_token_ref())
629
0
    }
630
631
    /// Same as `Parser::next`, but does not skip whitespace or comment tokens.
632
    ///
633
    /// **Note**: This should only be used in contexts like a CSS pre-processor
634
    /// where comments are preserved.
635
    /// When parsing higher-level values, per the CSS Syntax specification,
636
    /// comments should always be ignored between tokens.
637
0
    pub fn next_including_whitespace_and_comments(
638
0
        &mut self,
639
0
    ) -> Result<&Token<'i>, BasicParseError<'i>> {
640
0
        if let Some(block_type) = self.at_start_of.take() {
641
0
            consume_until_end_of_block(block_type, &mut self.input.tokenizer);
642
0
        }
643
644
0
        let byte = self.input.tokenizer.next_byte();
645
0
        if self.stop_before.contains(Delimiters::from_byte(byte)) {
646
0
            return Err(self.new_basic_error(BasicParseErrorKind::EndOfInput));
647
0
        }
648
649
0
        let token_start_position = self.input.tokenizer.position();
650
0
        let using_cached_token = self
651
0
            .input
652
0
            .cached_token
653
0
            .as_ref()
654
0
            .map_or(false, |cached_token| {
655
0
                cached_token.start_position == token_start_position
656
0
            });
657
0
        let token = if using_cached_token {
658
0
            let cached_token = self.input.cached_token.as_ref().unwrap();
659
0
            self.input.tokenizer.reset(&cached_token.end_state);
660
0
            if let Token::Function(ref name) = cached_token.token {
661
0
                self.input.tokenizer.see_function(name)
662
0
            }
663
0
            &cached_token.token
664
        } else {
665
0
            let new_token = self
666
0
                .input
667
0
                .tokenizer
668
0
                .next()
669
0
                .map_err(|()| self.new_basic_error(BasicParseErrorKind::EndOfInput))?;
670
0
            self.input.cached_token = Some(CachedToken {
671
0
                token: new_token,
672
0
                start_position: token_start_position,
673
0
                end_state: self.input.tokenizer.state(),
674
0
            });
675
0
            self.input.cached_token_ref()
676
        };
677
678
0
        if let Some(block_type) = BlockType::opening(token) {
679
0
            self.at_start_of = Some(block_type);
680
0
        }
681
0
        Ok(token)
682
0
    }
683
684
    /// Have the given closure parse something, then check the the input is exhausted.
685
    /// The result is overridden to an `Err(..)` if some input remains.
686
    ///
687
    /// This can help tell e.g. `color: green;` from `color: green 4px;`
688
    #[inline]
689
0
    pub fn parse_entirely<F, T, E>(&mut self, parse: F) -> Result<T, ParseError<'i, E>>
690
0
    where
691
0
        F: FnOnce(&mut Parser<'i, 't>) -> Result<T, ParseError<'i, E>>,
692
    {
693
0
        let result = parse(self)?;
694
0
        self.expect_exhausted()?;
695
0
        Ok(result)
696
0
    }
Unexecuted instantiation: <cssparser::parser::Parser>::parse_entirely::<<ammonia::style::Declarations as cssparser::rules_and_declarations::DeclarationParser>::parse_value::{closure#0}, (), ()>
Unexecuted instantiation: <cssparser::parser::Parser>::parse_entirely::<_, _, _>
697
698
    /// Parse a list of comma-separated values, all with the same syntax.
699
    ///
700
    /// The given closure is called repeatedly with a "delimited" parser
701
    /// (see the `Parser::parse_until_before` method) so that it can over
702
    /// consume the input past a comma at this block/function nesting level.
703
    ///
704
    /// Successful results are accumulated in a vector.
705
    ///
706
    /// This method returns an`Err(..)` the first time that a closure call does,
707
    /// or if a closure call leaves some input before the next comma or the end
708
    /// of the input.
709
    #[inline]
710
0
    pub fn parse_comma_separated<F, T, E>(
711
0
        &mut self,
712
0
        parse_one: F,
713
0
    ) -> Result<Vec<T>, ParseError<'i, E>>
714
0
    where
715
0
        F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
716
    {
717
0
        self.parse_comma_separated_internal(parse_one, /* ignore_errors = */ false)
718
0
    }
719
720
    /// Like `parse_comma_separated`, but ignores errors on unknown components,
721
    /// rather than erroring out in the whole list.
722
    ///
723
    /// Caller must deal with the fact that the resulting list might be empty,
724
    /// if there's no valid component on the list.
725
    #[inline]
726
0
    pub fn parse_comma_separated_ignoring_errors<F, T, E: 'i>(&mut self, parse_one: F) -> Vec<T>
727
0
    where
728
0
        F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
729
    {
730
0
        match self.parse_comma_separated_internal(parse_one, /* ignore_errors = */ true) {
731
0
            Ok(values) => values,
732
0
            Err(..) => unreachable!(),
733
        }
734
0
    }
735
736
    #[inline]
737
0
    fn parse_comma_separated_internal<F, T, E>(
738
0
        &mut self,
739
0
        mut parse_one: F,
740
0
        ignore_errors: bool,
741
0
    ) -> Result<Vec<T>, ParseError<'i, E>>
742
0
    where
743
0
        F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
744
    {
745
        // Vec grows from 0 to 4 by default on first push().  So allocate with
746
        // capacity 1, so in the somewhat common case of only one item we don't
747
        // way overallocate.  Note that we always push at least one item if
748
        // parsing succeeds.
749
0
        let mut values = Vec::with_capacity(1);
750
        loop {
751
0
            self.skip_whitespace(); // Unnecessary for correctness, but may help try() in parse_one rewind less.
752
0
            match self.parse_until_before(Delimiter::Comma, &mut parse_one) {
753
0
                Ok(v) => values.push(v),
754
0
                Err(e) if !ignore_errors => return Err(e),
755
0
                Err(_) => {}
756
            }
757
0
            match self.next() {
758
0
                Err(_) => return Ok(values),
759
0
                Ok(&Token::Comma) => continue,
760
0
                Ok(_) => unreachable!(),
761
            }
762
        }
763
0
    }
764
765
    /// Parse the content of a block or function.
766
    ///
767
    /// This method panics if the last token yielded by this parser
768
    /// (from one of the `next*` methods)
769
    /// is not a on that marks the start of a block or function:
770
    /// a `Function`, `ParenthesisBlock`, `CurlyBracketBlock`, or `SquareBracketBlock`.
771
    ///
772
    /// The given closure is called with a "delimited" parser
773
    /// that stops at the end of the block or function (at the matching closing token).
774
    ///
775
    /// The result is overridden to an `Err(..)` if the closure leaves some input before that point.
776
    #[inline]
777
0
    pub fn parse_nested_block<F, T, E>(&mut self, parse: F) -> Result<T, ParseError<'i, E>>
778
0
    where
779
0
        F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
780
    {
781
0
        parse_nested_block(self, parse)
782
0
    }
Unexecuted instantiation: <cssparser::parser::Parser>::parse_nested_block::<<ammonia::style::Declarations as cssparser::rules_and_declarations::DeclarationParser>::parse_value::{closure#0}, (), ()>
Unexecuted instantiation: <cssparser::parser::Parser>::parse_nested_block::<_, _, _>
783
784
    /// Limit parsing to until a given delimiter or the end of the input. (E.g.
785
    /// a semicolon for a property value.)
786
    ///
787
    /// The given closure is called with a "delimited" parser
788
    /// that stops before the first character at this block/function nesting level
789
    /// that matches the given set of delimiters, or at the end of the input.
790
    ///
791
    /// The result is overridden to an `Err(..)` if the closure leaves some input before that point.
792
    #[inline]
793
0
    pub fn parse_until_before<F, T, E>(
794
0
        &mut self,
795
0
        delimiters: Delimiters,
796
0
        parse: F,
797
0
    ) -> Result<T, ParseError<'i, E>>
798
0
    where
799
0
        F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
800
    {
801
0
        parse_until_before(self, delimiters, ParseUntilErrorBehavior::Consume, parse)
802
0
    }
803
804
    /// Like `parse_until_before`, but also consume the delimiter token.
805
    ///
806
    /// This can be useful when you don’t need to know which delimiter it was
807
    /// (e.g. if these is only one in the given set)
808
    /// or if it was there at all (as opposed to reaching the end of the input).
809
    #[inline]
810
0
    pub fn parse_until_after<F, T, E>(
811
0
        &mut self,
812
0
        delimiters: Delimiters,
813
0
        parse: F,
814
0
    ) -> Result<T, ParseError<'i, E>>
815
0
    where
816
0
        F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
817
    {
818
0
        parse_until_after(self, delimiters, ParseUntilErrorBehavior::Consume, parse)
819
0
    }
820
821
    /// Parse a <whitespace-token> and return its value.
822
    #[inline]
823
0
    pub fn expect_whitespace(&mut self) -> Result<&'i str, BasicParseError<'i>> {
824
0
        let start_location = self.current_source_location();
825
0
        match *self.next_including_whitespace()? {
826
0
            Token::WhiteSpace(value) => Ok(value),
827
0
            ref t => Err(start_location.new_basic_unexpected_token_error(t.clone())),
828
        }
829
0
    }
830
831
    /// Parse a <ident-token> and return the unescaped value.
832
    #[inline]
833
0
    pub fn expect_ident(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
834
0
        expect! {self,
835
0
            Token::Ident(ref value) => Ok(value),
836
        }
837
0
    }
Unexecuted instantiation: <cssparser::parser::Parser>::expect_ident
Unexecuted instantiation: <cssparser::parser::Parser>::expect_ident
838
839
    /// expect_ident, but clone the CowRcStr
840
    #[inline]
841
0
    pub fn expect_ident_cloned(&mut self) -> Result<CowRcStr<'i>, BasicParseError<'i>> {
842
0
        self.expect_ident().cloned()
843
0
    }
844
845
    /// Parse a <ident-token> whose unescaped value is an ASCII-insensitive match for the given value.
846
    #[inline]
847
0
    pub fn expect_ident_matching(
848
0
        &mut self,
849
0
        expected_value: &str,
850
0
    ) -> Result<(), BasicParseError<'i>> {
851
0
        expect! {self,
852
0
            Token::Ident(ref value) if value.eq_ignore_ascii_case(expected_value) => Ok(()),
853
        }
854
0
    }
855
856
    /// Parse a <string-token> and return the unescaped value.
857
    #[inline]
858
0
    pub fn expect_string(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
859
0
        expect! {self,
860
0
            Token::QuotedString(ref value) => Ok(value),
861
        }
862
0
    }
863
864
    /// expect_string, but clone the CowRcStr
865
    #[inline]
866
0
    pub fn expect_string_cloned(&mut self) -> Result<CowRcStr<'i>, BasicParseError<'i>> {
867
0
        self.expect_string().cloned()
868
0
    }
869
870
    /// Parse either a <ident-token> or a <string-token>, and return the unescaped value.
871
    #[inline]
872
0
    pub fn expect_ident_or_string(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
873
0
        expect! {self,
874
0
            Token::Ident(ref value) => Ok(value),
875
0
            Token::QuotedString(ref value) => Ok(value),
876
        }
877
0
    }
878
879
    /// Parse a <url-token> and return the unescaped value.
880
    #[inline]
881
0
    pub fn expect_url(&mut self) -> Result<CowRcStr<'i>, BasicParseError<'i>> {
882
0
        expect! {self,
883
0
            Token::UnquotedUrl(ref value) => Ok(value.clone()),
884
0
            Token::Function(ref name) if name.eq_ignore_ascii_case("url") => {
885
0
                self.parse_nested_block(|input| {
886
0
                    input.expect_string().map_err(Into::into).cloned()
887
0
                })
888
0
                .map_err(ParseError::<()>::basic)
889
            }
890
        }
891
0
    }
892
893
    /// Parse either a <url-token> or a <string-token>, and return the unescaped value.
894
    #[inline]
895
0
    pub fn expect_url_or_string(&mut self) -> Result<CowRcStr<'i>, BasicParseError<'i>> {
896
0
        expect! {self,
897
0
            Token::UnquotedUrl(ref value) => Ok(value.clone()),
898
0
            Token::QuotedString(ref value) => Ok(value.clone()),
899
0
            Token::Function(ref name) if name.eq_ignore_ascii_case("url") => {
900
0
                self.parse_nested_block(|input| {
901
0
                    input.expect_string().map_err(Into::into).cloned()
902
0
                })
903
0
                .map_err(ParseError::<()>::basic)
904
            }
905
        }
906
0
    }
907
908
    /// Parse a <number-token> and return the integer value.
909
    #[inline]
910
0
    pub fn expect_number(&mut self) -> Result<f32, BasicParseError<'i>> {
911
0
        expect! {self,
912
0
            Token::Number { value, .. } => Ok(value),
913
        }
914
0
    }
915
916
    /// Parse a <number-token> that does not have a fractional part, and return the integer value.
917
    #[inline]
918
0
    pub fn expect_integer(&mut self) -> Result<i32, BasicParseError<'i>> {
919
0
        expect! {self,
920
0
            Token::Number { int_value: Some(int_value), .. } => Ok(int_value),
921
        }
922
0
    }
923
924
    /// Parse a <percentage-token> and return the value.
925
    /// `0%` and `100%` map to `0.0` and `1.0` (not `100.0`), respectively.
926
    #[inline]
927
0
    pub fn expect_percentage(&mut self) -> Result<f32, BasicParseError<'i>> {
928
0
        expect! {self,
929
0
            Token::Percentage { unit_value, .. } => Ok(unit_value),
930
        }
931
0
    }
932
933
    /// Parse a `:` <colon-token>.
934
    #[inline]
935
0
    pub fn expect_colon(&mut self) -> Result<(), BasicParseError<'i>> {
936
0
        expect! {self,
937
0
            Token::Colon => Ok(()),
938
        }
939
0
    }
Unexecuted instantiation: <cssparser::parser::Parser>::expect_colon
Unexecuted instantiation: <cssparser::parser::Parser>::expect_colon
940
941
    /// Parse a `;` <semicolon-token>.
942
    #[inline]
943
0
    pub fn expect_semicolon(&mut self) -> Result<(), BasicParseError<'i>> {
944
0
        expect! {self,
945
0
            Token::Semicolon => Ok(()),
946
        }
947
0
    }
948
949
    /// Parse a `,` <comma-token>.
950
    #[inline]
951
0
    pub fn expect_comma(&mut self) -> Result<(), BasicParseError<'i>> {
952
0
        expect! {self,
953
0
            Token::Comma => Ok(()),
954
        }
955
0
    }
956
957
    /// Parse a <delim-token> with the given value.
958
    #[inline]
959
0
    pub fn expect_delim(&mut self, expected_value: char) -> Result<(), BasicParseError<'i>> {
960
0
        expect! {self,
961
0
            Token::Delim(value) if value == expected_value => Ok(()),
962
        }
963
0
    }
964
965
    /// Parse a `{ /* ... */ }` curly brackets block.
966
    ///
967
    /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method.
968
    #[inline]
969
0
    pub fn expect_curly_bracket_block(&mut self) -> Result<(), BasicParseError<'i>> {
970
0
        expect! {self,
971
0
            Token::CurlyBracketBlock => Ok(()),
972
        }
973
0
    }
974
975
    /// Parse a `[ /* ... */ ]` square brackets block.
976
    ///
977
    /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method.
978
    #[inline]
979
0
    pub fn expect_square_bracket_block(&mut self) -> Result<(), BasicParseError<'i>> {
980
0
        expect! {self,
981
0
            Token::SquareBracketBlock => Ok(()),
982
        }
983
0
    }
984
985
    /// Parse a `( /* ... */ )` parenthesis block.
986
    ///
987
    /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method.
988
    #[inline]
989
0
    pub fn expect_parenthesis_block(&mut self) -> Result<(), BasicParseError<'i>> {
990
0
        expect! {self,
991
0
            Token::ParenthesisBlock => Ok(()),
992
        }
993
0
    }
994
995
    /// Parse a <function> token and return its name.
996
    ///
997
    /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method.
998
    #[inline]
999
0
    pub fn expect_function(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
1000
0
        expect! {self,
1001
0
            Token::Function(ref name) => Ok(name),
1002
        }
1003
0
    }
1004
1005
    /// Parse a <function> token whose name is an ASCII-insensitive match for the given value.
1006
    ///
1007
    /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method.
1008
    #[inline]
1009
0
    pub fn expect_function_matching(
1010
0
        &mut self,
1011
0
        expected_name: &str,
1012
0
    ) -> Result<(), BasicParseError<'i>> {
1013
0
        expect! {self,
1014
0
            Token::Function(ref name) if name.eq_ignore_ascii_case(expected_name) => Ok(()),
1015
        }
1016
0
    }
1017
1018
    /// Parse the input until exhaustion and check that it contains no “error” token.
1019
    ///
1020
    /// See `Token::is_parse_error`. This also checks nested blocks and functions recursively.
1021
    #[inline]
1022
0
    pub fn expect_no_error_token(&mut self) -> Result<(), BasicParseError<'i>> {
1023
        loop {
1024
0
            match self.next_including_whitespace_and_comments() {
1025
                Ok(&Token::Function(_))
1026
                | Ok(&Token::ParenthesisBlock)
1027
                | Ok(&Token::SquareBracketBlock)
1028
0
                | Ok(&Token::CurlyBracketBlock) => self
1029
0
                    .parse_nested_block(|input| input.expect_no_error_token().map_err(Into::into))
1030
0
                    .map_err(ParseError::<()>::basic)?,
1031
0
                Ok(t) => {
1032
                    // FIXME: maybe these should be separate variants of
1033
                    // BasicParseError instead?
1034
0
                    if t.is_parse_error() {
1035
0
                        let token = t.clone();
1036
0
                        return Err(self.new_basic_unexpected_token_error(token));
1037
0
                    }
1038
                }
1039
0
                Err(_) => return Ok(()),
1040
            }
1041
        }
1042
0
    }
1043
}
1044
1045
0
pub fn parse_until_before<'i: 't, 't, F, T, E>(
1046
0
    parser: &mut Parser<'i, 't>,
1047
0
    delimiters: Delimiters,
1048
0
    error_behavior: ParseUntilErrorBehavior,
1049
0
    parse: F,
1050
0
) -> Result<T, ParseError<'i, E>>
1051
0
where
1052
0
    F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
1053
{
1054
0
    let delimiters = parser.stop_before | delimiters;
1055
    let result;
1056
    // Introduce a new scope to limit duration of nested_parser’s borrow
1057
    {
1058
0
        let mut delimited_parser = Parser {
1059
0
            input: parser.input,
1060
0
            at_start_of: parser.at_start_of.take(),
1061
0
            stop_before: delimiters,
1062
0
        };
1063
0
        result = delimited_parser.parse_entirely(parse);
1064
0
        if error_behavior == ParseUntilErrorBehavior::Stop && result.is_err() {
1065
0
            return result;
1066
0
        }
1067
0
        if let Some(block_type) = delimited_parser.at_start_of {
1068
0
            consume_until_end_of_block(block_type, &mut delimited_parser.input.tokenizer);
1069
0
        }
1070
    }
1071
    // FIXME: have a special-purpose tokenizer method for this that does less work.
1072
    loop {
1073
0
        if delimiters.contains(Delimiters::from_byte(parser.input.tokenizer.next_byte())) {
1074
0
            break;
1075
0
        }
1076
0
        if let Ok(token) = parser.input.tokenizer.next() {
1077
0
            if let Some(block_type) = BlockType::opening(&token) {
1078
0
                consume_until_end_of_block(block_type, &mut parser.input.tokenizer);
1079
0
            }
1080
        } else {
1081
0
            break;
1082
        }
1083
    }
1084
0
    result
1085
0
}
1086
1087
0
pub fn parse_until_after<'i: 't, 't, F, T, E>(
1088
0
    parser: &mut Parser<'i, 't>,
1089
0
    delimiters: Delimiters,
1090
0
    error_behavior: ParseUntilErrorBehavior,
1091
0
    parse: F,
1092
0
) -> Result<T, ParseError<'i, E>>
1093
0
where
1094
0
    F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
1095
{
1096
0
    let result = parse_until_before(parser, delimiters, error_behavior, parse);
1097
0
    if error_behavior == ParseUntilErrorBehavior::Stop && result.is_err() {
1098
0
        return result;
1099
0
    }
1100
0
    let next_byte = parser.input.tokenizer.next_byte();
1101
0
    if next_byte.is_some()
1102
0
        && !parser
1103
0
            .stop_before
1104
0
            .contains(Delimiters::from_byte(next_byte))
1105
    {
1106
0
        debug_assert!(delimiters.contains(Delimiters::from_byte(next_byte)));
1107
        // We know this byte is ASCII.
1108
0
        parser.input.tokenizer.advance(1);
1109
0
        if next_byte == Some(b'{') {
1110
0
            consume_until_end_of_block(BlockType::CurlyBracket, &mut parser.input.tokenizer);
1111
0
        }
1112
0
    }
1113
0
    result
1114
0
}
1115
1116
0
pub fn parse_nested_block<'i: 't, 't, F, T, E>(
1117
0
    parser: &mut Parser<'i, 't>,
1118
0
    parse: F,
1119
0
) -> Result<T, ParseError<'i, E>>
1120
0
where
1121
0
    F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
1122
{
1123
0
    let block_type = parser.at_start_of.take().expect(
1124
0
        "\
1125
0
         A nested parser can only be created when a Function, \
1126
0
         ParenthesisBlock, SquareBracketBlock, or CurlyBracketBlock \
1127
0
         token was just consumed.\
1128
0
         ",
1129
    );
1130
0
    let closing_delimiter = match block_type {
1131
0
        BlockType::CurlyBracket => ClosingDelimiter::CloseCurlyBracket,
1132
0
        BlockType::SquareBracket => ClosingDelimiter::CloseSquareBracket,
1133
0
        BlockType::Parenthesis => ClosingDelimiter::CloseParenthesis,
1134
    };
1135
    let result;
1136
    // Introduce a new scope to limit duration of nested_parser’s borrow
1137
    {
1138
0
        let mut nested_parser = Parser {
1139
0
            input: parser.input,
1140
0
            at_start_of: None,
1141
0
            stop_before: closing_delimiter,
1142
0
        };
1143
0
        result = nested_parser.parse_entirely(parse);
1144
0
        if let Some(block_type) = nested_parser.at_start_of {
1145
0
            consume_until_end_of_block(block_type, &mut nested_parser.input.tokenizer);
1146
0
        }
1147
    }
1148
0
    consume_until_end_of_block(block_type, &mut parser.input.tokenizer);
1149
0
    result
1150
0
}
Unexecuted instantiation: cssparser::parser::parse_nested_block::<<ammonia::style::Declarations as cssparser::rules_and_declarations::DeclarationParser>::parse_value::{closure#0}, (), ()>
Unexecuted instantiation: cssparser::parser::parse_nested_block::<_, _, _>
1151
1152
#[inline(never)]
1153
#[cold]
1154
0
fn consume_until_end_of_block(block_type: BlockType, tokenizer: &mut Tokenizer) {
1155
0
    let mut stack = SmallVec::<[BlockType; 16]>::new();
1156
0
    stack.push(block_type);
1157
1158
    // FIXME: have a special-purpose tokenizer method for this that does less work.
1159
0
    while let Ok(ref token) = tokenizer.next() {
1160
0
        if let Some(b) = BlockType::closing(token) {
1161
0
            if *stack.last().unwrap() == b {
1162
0
                stack.pop();
1163
0
                if stack.is_empty() {
1164
0
                    return;
1165
0
                }
1166
0
            }
1167
0
        }
1168
1169
0
        if let Some(block_type) = BlockType::opening(token) {
1170
0
            stack.push(block_type);
1171
0
        }
1172
    }
1173
0
}