1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use std::mem::take;

use serde::{Deserialize, Serialize};
use swc_css_ast::*;

use self::input::{Buffer, ParserInput};
use crate::{error::Error, Parse};

#[macro_use]
mod macros;
mod at_rules;
pub mod input;
mod selectors;
mod syntax;
#[cfg(test)]
mod tests;
mod util;
mod values_and_units;

pub type PResult<T> = Result<T, Error>;

#[derive(
    Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
#[serde(rename_all = "camelCase")]
pub struct ParserConfig {
    /// If this is `true`, **wrong** comments starting with `//` will be treated
    /// as a comment.
    ///
    /// This option exists because there are so many css-in-js tools and people
    /// use `//` as a comment because it's javascript file.
    ///
    /// Defaults to `false`.
    #[serde(default)]
    pub allow_wrong_line_comments: bool,

    /// If enabled, errors for css modules selectors will be ignored.
    ///
    ///
    /// Defaults to `false`.
    #[serde(default)]
    pub css_modules: bool,

    /// If this is `true`, the nested selector starts with an identifier will be
    /// parsed as valid selectors (i.e. `ul { color: red; li { color: blue } }`)
    ///
    /// Defaults to `false`.
    #[serde(default)]
    pub legacy_nesting: bool,

    /// If this is `true`, the legacy syntax for IE will be parsed.
    #[serde(default)]
    pub legacy_ie: bool,
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum BlockContentsGrammar {
    StyleBlock,
    DeclarationList,
    RuleList,
    #[default]
    Stylesheet,
}

#[derive(Debug, Clone, Copy)]
struct Ctx {
    is_top_level: bool,
    block_contents_grammar: BlockContentsGrammar,
    mixed_with_declarations: bool,
    need_canonicalize: bool,

    in_keyframes_at_rule: bool,
    in_supports_at_rule: bool,
    in_import_at_rule: bool,
    in_page_at_rule: bool,
    in_container_at_rule: bool,
    in_font_feature_values_at_rule: bool,

    in_global_or_local_selector: bool,
}

impl Default for Ctx {
    fn default() -> Self {
        Ctx {
            is_top_level: false,
            block_contents_grammar: BlockContentsGrammar::default(),
            mixed_with_declarations: false,
            need_canonicalize: true,

            in_keyframes_at_rule: false,
            in_supports_at_rule: false,
            in_import_at_rule: false,
            in_page_at_rule: false,
            in_container_at_rule: false,
            in_font_feature_values_at_rule: false,
            in_global_or_local_selector: false,
        }
    }
}

#[derive(Debug, Clone)]
pub struct Parser<I>
where
    I: ParserInput,
{
    #[allow(dead_code)]
    config: ParserConfig,
    input: Buffer<I>,
    ctx: Ctx,
    errors: Vec<Error>,
}

impl<I> Parser<I>
where
    I: ParserInput,
{
    pub fn new(input: I, config: ParserConfig) -> Self {
        Parser {
            config,
            input: Buffer::new(input),
            ctx: Default::default(),
            errors: Default::default(),
        }
    }

    pub fn dump_cur(&mut self) -> String {
        format!("{:?}", self.input.cur())
    }

    /// Take **recovered** errors.
    pub fn take_errors(&mut self) -> Vec<Error> {
        take(&mut self.errors)
    }

    pub fn parse_all(&mut self) -> PResult<Stylesheet> {
        self.parse()
    }
}