Coverage Report

Created: 2026-10-10 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rust-cssparser/src/nth.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 super::{BasicParseError, Parser, Token};
6
7
/// Parse the *An+B* notation, as found in the `:nth-child()` selector.
8
/// The input is typically the arguments of a function,
9
/// in which case the caller needs to check if the arguments’ parser is exhausted.
10
/// Return `Ok((A, B))`, or an `Err(..)` for a syntax error.
11
pub fn parse_nth(input: &mut Parser) -> Result<(i32, i32), BasicParseError> {
12
    match *input.next()? {
13
        Token::Number {
14
            int_value: Some(b), ..
15
        } => Ok((0, b)),
16
        Token::Dimension {
17
            int_value: Some(a),
18
            ref unit,
19
            ..
20
        } => {
21
            match_ignore_ascii_case! {
22
                unit,
23
                "n" => parse_b(input, a),
24
                "n-" => parse_signless_b(input, a, -1),
25
                _ => match parse_n_dash_digits(unit) {
26
                    Ok(b) => Ok((a, b)),
27
                    Err(()) => {
28
                        Err(BasicParseError::unexpected_token())
29
                    }
30
                }
31
            }
32
        }
33
        Token::Ident(ref value) => {
34
            match_ignore_ascii_case! { value,
35
                "even" => Ok((2, 0)),
36
                "odd" => Ok((2, 1)),
37
                "n" => parse_b(input, 1),
38
                "-n" => parse_b(input, -1),
39
                "n-" => parse_signless_b(input, 1, -1),
40
                "-n-" => parse_signless_b(input, -1, -1),
41
                _ => {
42
                    let (slice, a) = if let Some(stripped) = value.strip_prefix('-') {
43
                        (stripped, -1)
44
                    } else {
45
                        (&**value, 1)
46
                    };
47
                    match parse_n_dash_digits(slice) {
48
                        Ok(b) => Ok((a, b)),
49
                        Err(()) => {
50
                            Err(BasicParseError::unexpected_token())
51
                        }
52
                    }
53
                }
54
            }
55
        }
56
        Token::Delim('+') => match *input.next_including_whitespace()? {
57
            Token::Ident(ref value) => {
58
                match_ignore_ascii_case! { value,
59
                    "n" => parse_b(input, 1),
60
                    "n-" => parse_signless_b(input, 1, -1),
61
                    _ => match parse_n_dash_digits(value) {
62
                        Ok(b) => Ok((1, b)),
63
                        Err(()) => {
64
                            Err(BasicParseError::unexpected_token())
65
                        }
66
                    }
67
                }
68
            }
69
            _ => Err(BasicParseError::unexpected_token()),
70
        },
71
        _ => Err(BasicParseError::unexpected_token()),
72
    }
73
}
74
75
fn parse_b(input: &mut Parser, a: i32) -> Result<(i32, i32), BasicParseError> {
76
    let start = input.state();
77
    match input.next() {
78
        Ok(&Token::Delim('+')) => parse_signless_b(input, a, 1),
79
        Ok(&Token::Delim('-')) => parse_signless_b(input, a, -1),
80
        Ok(&Token::Number {
81
            has_sign: true,
82
            int_value: Some(b),
83
            ..
84
        }) => Ok((a, b)),
85
        _ => {
86
            input.reset(&start);
87
            Ok((a, 0))
88
        }
89
    }
90
}
91
92
fn parse_signless_b(
93
    input: &mut Parser,
94
    a: i32,
95
    b_sign: i32,
96
) -> Result<(i32, i32), BasicParseError> {
97
    match input.next()? {
98
        &Token::Number {
99
            has_sign: false,
100
            int_value: Some(b),
101
            ..
102
        } => Ok((a, b_sign * b)),
103
        _ => Err(BasicParseError::unexpected_token()),
104
    }
105
}
106
107
fn parse_n_dash_digits(string: &str) -> Result<i32, ()> {
108
    let bytes = string.as_bytes();
109
    if bytes.len() >= 3
110
        && bytes[..2].eq_ignore_ascii_case(b"n-")
111
0
        && bytes[2..].iter().all(|&c| c.is_ascii_digit())
112
    {
113
        Ok(parse_number_saturate(&string[1..]).unwrap()) // Include the minus sign
114
    } else {
115
        Err(())
116
    }
117
}
118
119
fn parse_number_saturate(string: &str) -> Result<i32, ()> {
120
    let mut parser = Parser::new(string);
121
    let int = if let Ok(&Token::Number {
122
        int_value: Some(int),
123
        ..
124
    }) = parser.next_including_whitespace_and_comments()
125
    {
126
        int
127
    } else {
128
        return Err(());
129
    };
130
    if !parser.is_exhausted() {
131
        return Err(());
132
    }
133
    Ok(int)
134
}