css_modules/
parser.rs

1use pest::iterators::Pair;
2use pest::iterators::Pairs;
3use pest::Parser;
4use pest_derive::*;
5use std::fmt;
6
7#[derive(Parser)]
8#[grammar = "parser.pest"]
9struct Grammar;
10
11pub type ParserResult<'p, T> = Result<T, Error>;
12
13#[derive(Debug, PartialEq)]
14pub enum Error {
15    Parser(pest::error::Error<Rule>),
16    AtPair(String),
17}
18
19impl fmt::Display for Error {
20    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
21        match self {
22            Error::Parser(error) => write!(formatter, "{}", error),
23            Error::AtPair(error) => write!(formatter, "{}", error),
24        }
25    }
26}
27
28impl<'p> From<Pair<'p, Rule>> for Error {
29    fn from(pair: Pair<'p, Rule>) -> Self {
30        let message = match pair.as_rule() {
31            Rule::error_block_not_terminated => "Unterminated ruleset",
32            _ => "Unexpected error",
33        };
34        let start = pair.as_span().start_pos();
35        let line = start.line_of();
36        let (line_no, col_no) = start.line_col();
37        let line_no_len = format!("{}", line_no).len();
38        let mut spacing = String::new();
39
40        for _ in 0..line_no_len {
41            spacing.push(' ');
42        }
43
44        Error::AtPair(format!(
45            " {line_no:indent$} ┊ {line}\n    {spacing:col_no$}│\n    {spacing:col_no$}╰ {message} at {line_no}:{col_no}",
46            spacing = spacing,
47            indent = spacing.len(),
48            col_no = col_no,
49            line_no = line_no,
50            line = line,
51            message = message,
52        ))
53    }
54}
55
56fn parse<'p>(rule: Rule, input: &'p str) -> ParserResult<Pairs<'p, Rule>> {
57    match Grammar::parse(rule, input) {
58        Ok(pairs) => Ok(pairs),
59        Err(error) => Err(Error::Parser(error)),
60    }
61}
62
63pub fn animation<'p>(animation: &'p str) -> ParserResult<Pairs<'p, Rule>> {
64    parse(Rule::animation, animation)
65}
66
67pub fn stylesheet<'p>(stylesheet: &'p str) -> ParserResult<Pairs<'p, Rule>> {
68    parse(Rule::stylesheet, stylesheet)
69}
70
71pub fn selector<'p>(selector: &'p str) -> ParserResult<Pairs<'p, Rule>> {
72    parse(Rule::selector, selector)
73}