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
use pest::iterators::Pair;
use pest::iterators::Pairs;
use pest::Parser;
use pest_derive::*;
use std::fmt;

#[derive(Parser)]
#[grammar = "grammar.pest"]
struct Grammar;

pub type ParserResult<'p, T> = Result<T, Error<'p>>;

#[derive(Debug, PartialEq)]
pub enum Error<'p> {
    Parser(pest::error::Error<Rule>),
    Pair(Pair<'p, Rule>),
}

impl<'p> fmt::Display for Error<'p> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::Parser(error) => write!(formatter, "{}", error),
            Error::Pair(pair) => {
                let message = match pair.as_rule() {
                    Rule::error_block_not_terminated => "Unterminated ruleset",
                    _ => "Unexpected error",
                };
                let start = pair.as_span().start_pos();
                let line = start.line_of();
                let (line_no, col_no) = start.line_col();
                let line_no_len = format!("{}", line_no).len();
                let mut spacing = String::new();

                for _ in 0..line_no_len {
                    spacing.push(' ');
                }

                write!(
                    formatter,
                    " {line_no:indent$} ┊ {line}\n    {spacing:col_no$}│\n    {spacing:col_no$}╰ {message} at {line_no}:{col_no}",
                    spacing = spacing,
                    indent = spacing.len(),
                    col_no = col_no,
                    line_no = line_no,
                    line = line,
                    message = message,
                )
            }
        }
    }
}

impl<'p> From<Pair<'p, Rule>> for Error<'p> {
    fn from(pair: Pair<'p, Rule>) -> Self {
        Error::Pair(pair)
    }
}

fn parse<'p>(rule: Rule, input: &'p str) -> ParserResult<Pairs<'p, Rule>> {
    match Grammar::parse(rule, input) {
        Ok(pairs) => Ok(pairs),
        Err(error) => Err(Error::Parser(error)),
    }
}

pub fn parse_animation<'p>(animation: &'p str) -> ParserResult<Pairs<'p, Rule>> {
    parse(Rule::animation, animation)
}

pub fn parse_stylesheet<'p>(stylesheet: &'p str) -> ParserResult<Pairs<'p, Rule>> {
    parse(Rule::stylesheet, stylesheet)
}

pub fn parse_selector<'p>(selector: &'p str) -> ParserResult<Pairs<'p, Rule>> {
    parse(Rule::selector, selector)
}