envuse_parser/utils/
display_syntax.rs

1use super::super::parser::span::Span;
2
3pub struct DisplaySyntaxDebugOptions {
4    pub location: Option<String>,
5    pub print_full: bool,
6    pub print_underscore_error: bool,
7}
8
9impl DisplaySyntaxDebugOptions {
10    pub fn new() -> Self {
11        Self {
12            print_full: false,
13            print_underscore_error: true,
14            location: None,
15        }
16    }
17}
18
19impl Default for DisplaySyntaxDebugOptions {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25#[derive(Debug, Clone)]
26pub struct Line {
27    line: usize,
28    str: String,
29    span: Span,
30}
31
32#[derive(Debug, Clone)]
33pub struct DisplaySyntax {
34    pub message: String,
35    pub span: Span,
36}
37
38impl DisplaySyntax {
39    pub fn new<T: ToString>(message: T, span: Span) -> Self {
40        Self {
41            message: message.to_string(),
42            span,
43        }
44    }
45
46    pub fn helper_create_lines<T: ToString>(payload: &T) -> Vec<Line> {
47        let mut lines: Vec<Line> = vec![];
48        let mut current_line = Line {
49            line: 1,
50            str: String::new(),
51            span: Span { start: 0, end: 0 },
52        };
53
54        for (index, ch) in payload.to_string().chars().enumerate() {
55            current_line.span.end = index;
56            if ch == '\n' {
57                lines.push(current_line.clone());
58                current_line = Line {
59                    line: current_line.line + 1,
60                    str: String::new(),
61                    span: Span {
62                        start: index + 1,
63                        end: index + 1,
64                    },
65                };
66                continue;
67            }
68            current_line.str.push(ch);
69        }
70        lines.push(current_line.clone());
71
72        lines
73    }
74
75    pub fn debug_payload<T: ToString>(&self, payload: &T) -> String {
76        Self::debug_payload_configurable(&self, payload, &Default::default())
77    }
78
79    pub fn debug_payload_configurable<T: ToString>(
80        &self,
81        payload: &T,
82        options: &DisplaySyntaxDebugOptions,
83    ) -> String {
84        let mut buff = String::new();
85        let mut at_line_pos: Option<String> = None;
86        buff.push_str(format!("{}\n", self.message).as_str());
87        buff.push('\n');
88
89        // dbg!(&self.span);
90        for line in Self::helper_create_lines(payload) {
91            let err_start_inline =
92                self.span.start >= line.span.start && self.span.start <= line.span.end;
93            let err_end_inline = self.span.end >= line.span.start && self.span.end <= line.span.end;
94            let err_cover_line: bool =
95                self.span.start <= line.span.start && self.span.end >= line.span.end;
96
97            if !options.print_full && !(err_start_inline || err_end_inline || err_cover_line) {
98                continue;
99            }
100
101            buff.push_str(
102                format!(
103                    "{} {:>4} | {}\n",
104                    {
105                        if err_start_inline || err_end_inline || err_cover_line {
106                            ">"
107                        } else {
108                            " "
109                        }
110                    },
111                    line.line.to_string().as_str(),
112                    // line.span.start,
113                    // line.span.end,
114                    line.str.as_str(),
115                )
116                .as_str(),
117            );
118
119            let err_subline_start: usize = {
120                if err_start_inline {
121                    self.span.start - line.span.start
122                } else {
123                    0
124                }
125            };
126
127            let err_subline_end: usize = {
128                if err_end_inline {
129                    (line.span.end - line.span.start) - (line.span.end - self.span.end)
130                } else {
131                    line.span.end - line.span.start
132                }
133            };
134
135            if options.print_underscore_error
136                && (err_start_inline || err_end_inline || err_cover_line)
137            {
138                buff.push_str(
139                    format!(
140                        "{}{}\n",
141                        " ".repeat(9 + err_subline_start),
142                        "▀".repeat(err_subline_end - err_subline_start)
143                    )
144                    .as_str(),
145                )
146            }
147
148            if at_line_pos.is_none() && (err_start_inline || err_end_inline || err_cover_line) {
149                at_line_pos = Some(format!(
150                    "{}:{}",
151                    // options.location.clone().unwrap(),
152                    line.line,
153                    err_subline_start + 1
154                ))
155            }
156        }
157
158        if let Some(pos) = at_line_pos {
159            buff.push_str(&format!(
160                "    at {}:{}\n",
161                options.location.clone().unwrap_or("<unknown>".to_string()),
162                pos,
163            ))
164        }
165
166        buff
167    }
168}