envuse_parser/utils/
display_syntax.rs1use 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 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.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 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}