]> git.proxmox.com Git - rustc.git/blame - vendor/winnow/src/trace/internals.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / vendor / winnow / src / trace / internals.rs
CommitLineData
49aad941
FG
1#![cfg(feature = "std")]
2
3use std::io::Write;
4
5use crate::error::ErrMode;
6use crate::stream::Stream;
781aab86
FG
7use crate::*;
8
9pub struct Trace<P, D, I, O, E>
10where
11 P: Parser<I, O, E>,
12 I: Stream,
13 D: std::fmt::Display,
14{
15 parser: P,
16 name: D,
17 call_count: usize,
18 i: core::marker::PhantomData<I>,
19 o: core::marker::PhantomData<O>,
20 e: core::marker::PhantomData<E>,
21}
22
23impl<P, D, I, O, E> Trace<P, D, I, O, E>
24where
25 P: Parser<I, O, E>,
26 I: Stream,
27 D: std::fmt::Display,
28{
29 #[inline(always)]
30 pub fn new(parser: P, name: D) -> Self {
31 Self {
32 parser,
33 name,
34 call_count: 0,
35 i: Default::default(),
36 o: Default::default(),
37 e: Default::default(),
38 }
39 }
40}
41
42impl<P, D, I, O, E> Parser<I, O, E> for Trace<P, D, I, O, E>
43where
44 P: Parser<I, O, E>,
45 I: Stream,
46 D: std::fmt::Display,
47{
48 #[inline]
49 fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
50 let depth = Depth::new();
51 let original = i.checkpoint();
52 start(*depth, &self.name, self.call_count, i);
53
54 let res = self.parser.parse_next(i);
55
56 let consumed = i.offset_from(&original);
57 let severity = Severity::with_result(&res);
58 end(*depth, &self.name, self.call_count, consumed, severity);
59 self.call_count += 1;
60
61 res
62 }
63}
49aad941
FG
64
65pub struct Depth {
66 depth: usize,
67 inc: bool,
68}
69
70impl Depth {
71 pub fn new() -> Self {
72 let depth = DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
73 let inc = true;
74 Self { depth, inc }
75 }
76
77 pub fn existing() -> Self {
78 let depth = DEPTH.load(std::sync::atomic::Ordering::SeqCst);
79 let inc = false;
80 Self { depth, inc }
81 }
82}
83
84impl Drop for Depth {
85 fn drop(&mut self) {
86 if self.inc {
87 let _ = DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
88 }
89 }
90}
91
92impl AsRef<usize> for Depth {
93 #[inline(always)]
94 fn as_ref(&self) -> &usize {
95 &self.depth
96 }
97}
98
99impl crate::lib::std::ops::Deref for Depth {
100 type Target = usize;
101
102 #[inline(always)]
103 fn deref(&self) -> &Self::Target {
104 &self.depth
105 }
106}
107
108static DEPTH: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
109
110pub enum Severity {
111 Success,
112 Backtrack,
113 Cut,
114 Incomplete,
115}
116
117impl Severity {
118 pub fn with_result<T, E>(result: &Result<T, ErrMode<E>>) -> Self {
119 match result {
120 Ok(_) => Self::Success,
121 Err(ErrMode::Backtrack(_)) => Self::Backtrack,
122 Err(ErrMode::Cut(_)) => Self::Cut,
123 Err(ErrMode::Incomplete(_)) => Self::Incomplete,
124 }
125 }
126}
127
128pub fn start<I: Stream>(
129 depth: usize,
130 name: &dyn crate::lib::std::fmt::Display,
131 count: usize,
132 input: &I,
133) {
fe692bf9
FG
134 let gutter_style = anstyle::Style::new().bold();
135 let input_style = anstyle::Style::new().underline();
136 let eof_style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Cyan.into()));
49aad941
FG
137
138 let (call_width, input_width) = column_widths();
139
140 let count = if 0 < count {
141 format!(":{count}")
142 } else {
143 "".to_owned()
144 };
145 let call_column = format!("{:depth$}> {name}{count}", "");
146
49aad941
FG
147 // The debug version of `slice` might be wider, either due to rendering one byte as two nibbles or
148 // escaping in strings.
add651ee 149 let mut debug_slice = format!("{:#?}", input.raw());
49aad941
FG
150 let (debug_slice, eof) = if let Some(debug_offset) = debug_slice
151 .char_indices()
152 .enumerate()
add651ee 153 .find_map(|(pos, (offset, _))| (input_width <= pos).then_some(offset))
49aad941
FG
154 {
155 debug_slice.truncate(debug_offset);
156 let eof = "";
157 (debug_slice, eof)
158 } else {
159 let eof = if debug_slice.chars().count() < input_width {
160 "∅"
161 } else {
162 ""
163 };
164 (debug_slice, eof)
165 };
166
781aab86 167 let writer = anstream::stderr();
49aad941 168 let mut writer = writer.lock();
fe692bf9
FG
169 let _ = writeln!(
170 writer,
171 "{call_column:call_width$} {gutter_style}|{gutter_reset} {input_style}{debug_slice}{input_reset}{eof_style}{eof}{eof_reset}",
172 gutter_style=gutter_style.render(),
173 gutter_reset=gutter_style.render_reset(),
174 input_style=input_style.render(),
175 input_reset=input_style.render_reset(),
176 eof_style=eof_style.render(),
177 eof_reset=eof_style.render_reset(),
178 );
49aad941
FG
179}
180
181pub fn end(
182 depth: usize,
183 name: &dyn crate::lib::std::fmt::Display,
184 count: usize,
add651ee 185 consumed: usize,
49aad941
FG
186 severity: Severity,
187) {
fe692bf9 188 let gutter_style = anstyle::Style::new().bold();
49aad941
FG
189
190 let (call_width, _) = column_widths();
191
192 let count = if 0 < count {
193 format!(":{count}")
194 } else {
195 "".to_owned()
196 };
197 let call_column = format!("{:depth$}< {name}{count}", "");
198
fe692bf9 199 let (status_style, status) = match severity {
49aad941 200 Severity::Success => {
fe692bf9 201 let style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into()));
add651ee 202 let status = format!("+{}", consumed);
49aad941
FG
203 (style, status)
204 }
205 Severity::Backtrack => (
fe692bf9 206 anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())),
49aad941
FG
207 "backtrack".to_owned(),
208 ),
209 Severity::Cut => (
fe692bf9 210 anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
49aad941
FG
211 "cut".to_owned(),
212 ),
213 Severity::Incomplete => (
fe692bf9 214 anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
49aad941
FG
215 "incomplete".to_owned(),
216 ),
217 };
49aad941 218
781aab86 219 let writer = anstream::stderr();
49aad941
FG
220 let mut writer = writer.lock();
221 let _ = writeln!(
222 writer,
fe692bf9
FG
223 "{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}",
224 gutter_style=gutter_style.render(),
225 gutter_reset=gutter_style.render_reset(),
226 status_style=status_style.render(),
227 status_reset=status_style.render_reset(),
49aad941
FG
228 );
229}
230
231pub fn result(depth: usize, name: &dyn crate::lib::std::fmt::Display, severity: Severity) {
fe692bf9 232 let gutter_style = anstyle::Style::new().bold();
49aad941
FG
233
234 let (call_width, _) = column_widths();
235
236 let call_column = format!("{:depth$}| {name}", "");
237
fe692bf9 238 let (status_style, status) = match severity {
49aad941 239 Severity::Success => (
fe692bf9 240 anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into())),
49aad941
FG
241 "",
242 ),
243 Severity::Backtrack => (
fe692bf9 244 anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())),
49aad941
FG
245 "backtrack",
246 ),
247 Severity::Cut => (
fe692bf9 248 anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
49aad941
FG
249 "cut",
250 ),
251 Severity::Incomplete => (
fe692bf9 252 anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
49aad941
FG
253 "incomplete",
254 ),
255 };
49aad941 256
781aab86 257 let writer = anstream::stderr();
49aad941
FG
258 let mut writer = writer.lock();
259 let _ = writeln!(
260 writer,
fe692bf9
FG
261 "{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}",
262 gutter_style=gutter_style.render(),
263 gutter_reset=gutter_style.render_reset(),
264 status_style=status_style.render(),
265 status_reset=status_style.render_reset(),
49aad941
FG
266 );
267}
268
49aad941
FG
269fn column_widths() -> (usize, usize) {
270 let term_width = term_width();
271
272 let min_call_width = 40;
273 let min_input_width = 20;
274 let decor_width = 3;
275 let extra_width = term_width
276 .checked_sub(min_call_width + min_input_width + decor_width)
277 .unwrap_or_default();
278 let call_width = min_call_width + 2 * extra_width / 3;
279 let input_width = min_input_width + extra_width / 3;
280
281 (call_width, input_width)
282}
283
284fn term_width() -> usize {
285 columns_env().or_else(query_width).unwrap_or(80)
286}
287
288fn query_width() -> Option<usize> {
289 use is_terminal::IsTerminal;
290 if std::io::stderr().is_terminal() {
291 terminal_size::terminal_size().map(|(w, _h)| w.0.into())
292 } else {
293 None
294 }
295}
296
297fn columns_env() -> Option<usize> {
298 std::env::var("COLUMNS")
299 .ok()
300 .and_then(|c| c.parse::<usize>().ok())
301}