1#[cfg(feature = "trace")]
5use crate::tags::TraceTags;
6#[cfg(feature = "trace-silencing")]
7use crate::traces::Trace;
8#[cfg(feature = "trace-context")]
9use nom::error::ContextError;
10use {
11 nom::{IResult, Parser},
12 std::fmt::Debug,
13};
14
15#[cfg(feature = "trace-color")]
16#[allow(dead_code)]
17pub(crate) mod ansi;
18#[cfg(feature = "trace")]
19pub mod events;
20#[cfg(feature = "trace")]
21pub mod tags;
22#[cfg(feature = "trace")]
23pub mod traces;
24
25pub mod macros;
26
27pub const DEFAULT_TAG: &str = "default";
28
29thread_local! {
30 #[cfg(feature = "trace")]
43 pub static TRACE_TAGS: std::cell::RefCell<TraceTags> = std::cell::RefCell::new(TraceTags::new());
44
45 #[cfg(feature = "trace-silencing")]
47 pub static TRACE_SILENT: std::cell::RefCell<Trace> = std::cell::RefCell::new(Trace::default());
48
49 #[cfg(feature = "trace-silencing")]
51 pub static TREE_SILENCE_LEVELS: std::cell::RefCell<Vec<usize>> = const { std::cell::RefCell::new(vec![]) };
52}
53
54#[cfg(feature = "trace-context")]
55pub trait TraceError<I>: Debug + ContextError<I> {}
56#[cfg(feature = "trace-context")]
57impl<I, E> TraceError<I> for E where E: Debug + ContextError<I> {}
58
59#[cfg(not(feature = "trace-context"))]
60pub trait TraceError<I>: Debug {}
61#[cfg(not(feature = "trace-context"))]
62impl<I, E> TraceError<I> for E where E: Debug {}
63
64pub fn tr<I, O, E, F>(
76 #[cfg(feature = "trace")] tag: &'static str,
77 #[cfg(not(feature = "trace"))] _tag: &'static str,
78 #[cfg(any(feature = "trace", feature = "trace-context"))] context: Option<&'static str>,
79 #[cfg(not(any(feature = "trace", feature = "trace-context")))] _context: Option<&'static str>,
80 #[cfg(feature = "trace")] name: &'static str,
81 #[cfg(not(feature = "trace"))] _name: &'static str,
82 mut parser: F,
83) -> impl FnMut(I) -> IResult<I, O, E>
84where
85 I: AsRef<str>,
86 F: Parser<I, O, E>,
87 I: Clone,
88 O: Debug,
89 E: TraceError<I>,
90{
91 #[cfg(feature = "trace")]
92 {
93 move |input: I| {
94 let input1 = input.clone();
95 let input2 = input.clone();
96 #[cfg(feature = "trace-context")]
97 let input3 = input.clone();
98
99 #[cfg(feature = "trace-silencing")]
100 let silent = TREE_SILENCE_LEVELS.with(|levels| !levels.borrow().is_empty());
101
102 #[cfg(feature = "trace-silencing")]
103 if silent {
104 TRACE_SILENT.with(|trace| {
105 (*trace.borrow_mut()).open(context, input1, name, true);
106 });
107 } else {
108 TRACE_TAGS.with(|tags| {
109 (*tags.borrow_mut()).open(tag, context, input1, name, false);
110 });
111 };
112 #[cfg(not(feature = "trace-silencing"))]
113 TRACE_TAGS.with(|tags| {
114 (*tags.borrow_mut()).open(tag, context, input1, name, false);
115 });
116
117 let res = parser.parse(input);
118
119 #[cfg(feature = "trace-silencing")]
120 if silent {
121 TRACE_SILENT.with(|trace| {
122 (*trace.borrow_mut()).close(context, input2, name, &res, true);
123 });
124 } else {
125 TRACE_TAGS.with(|tags| {
126 (*tags.borrow_mut()).close(tag, context, input2, name, &res, false);
127 });
128 }
129
130 #[cfg(not(feature = "trace-silencing"))]
131 TRACE_TAGS.with(|tags| {
132 (*tags.borrow_mut()).close(tag, context, input2, name, &res, false);
133 });
134
135 #[cfg(not(feature = "trace-context"))]
136 return res;
137
138 #[cfg(feature = "trace-context")]
139 if let Some(context) = context {
140 add_context_to_err(context, input3, res)
141 } else {
142 res
143 }
144 }
145 }
146
147 #[cfg(not(feature = "trace"))]
148 {
149 #[cfg(feature = "trace-context")]
150 {
151 move |input: I| {
152 if let Some(context) = context {
153 add_context_to_err(context, input.clone(), parser.parse(input))
154 } else {
155 parser.parse(input)
156 }
157 }
158 }
159
160 #[cfg(not(feature = "trace-context"))]
161 move |input: I| parser.parse(input)
162 }
163}
164
165#[cfg(feature = "trace-silencing")]
177pub fn silence_tree<I, O, E, F>(
178 tag: &'static str,
179 context: Option<&'static str>,
180 name: &'static str,
181 mut parser: F,
182) -> impl FnMut(I) -> IResult<I, O, E>
183where
184 I: AsRef<str>,
185 F: Parser<I, O, E>,
186 I: Clone,
187 O: Debug,
188 E: TraceError<I>,
189{
190 move |input: I| {
191 let input1 = input.clone();
192 let input2 = input.clone();
193 let input3 = input.clone();
194
195 let silent_cut_level = TRACE_SILENT.with(|tags| tags.borrow_mut().level);
196 let cut_level = TRACE_TAGS.with(|tags| (*tags.borrow_mut()).level_for_tag(tag));
197 let cut_level = silent_cut_level.max(cut_level);
198
199 TREE_SILENCE_LEVELS.with(|levels| {
200 (*levels.borrow_mut()).push(cut_level);
201 });
202
203 TRACE_SILENT.with(|trace| {
204 (*trace.borrow_mut()).set_level(cut_level);
205 (*trace.borrow_mut()).open(context, input1, name, true);
206 });
207
208 let res = parser.parse(input);
209
210 TRACE_SILENT.with(|trace| {
211 (*trace.borrow_mut()).close(context, input2, name, &res, true);
212 });
213
214 TREE_SILENCE_LEVELS.with(|levels| {
215 (*levels.borrow_mut()).pop();
216 });
217
218 #[cfg(feature = "trace-context")]
219 return add_context_to_err(name, input3, res);
220
221 #[cfg(not(feature = "trace-context"))]
222 res
223 }
224}
225
226#[cfg(feature = "trace-context")]
231fn add_context_to_err<I, O, E>(
232 name: &'static str,
233 input: I,
234 res: IResult<I, O, E>,
235) -> IResult<I, O, E>
236where
237 I: AsRef<str>,
238 I: Clone,
239 O: Debug,
240 E: TraceError<I>,
241{
242 match res {
243 Ok(o) => Ok(o),
244 Err(nom::Err::Error(e)) => Err(nom::Err::Error(E::add_context(input, name, e))),
245 Err(nom::Err::Failure(e)) => Err(nom::Err::Failure(E::add_context(input, name, e))),
246 Err(nom::Err::Incomplete(i)) => Err(nom::Err::Incomplete(i)),
247 }
248}
249
250pub fn get_trace_for_tag(
260 #[cfg(feature = "trace")] tag: &'static str,
261 #[cfg(not(feature = "trace"))] _tag: &'static str,
262) -> Option<String> {
263 #[cfg(feature = "trace")]
264 {
265 TRACE_TAGS.with(|trace| trace.borrow().traces.get(tag).map(ToString::to_string))
266 }
267
268 #[cfg(not(feature = "trace"))]
269 None
270}
271
272pub fn print_trace_for_tag(tag: &'static str) {
278 print(get_trace_for_tag(tag).unwrap_or(format!("No trace found for tag '{}'", tag)));
279}
280
281pub(crate) fn print<I: AsRef<str>>(s: I) {
287 use std::io::Write;
288 let stdout = std::io::stdout();
289 let mut handle = stdout.lock();
290 write!(handle, "{}", s.as_ref()).unwrap();
291}
292
293#[cfg(test)]
294mod tests {
295 use {
296 super::*,
297 nom::{bytes::complete::tag, error::VerboseError},
298 };
299
300 #[cfg(feature = "trace")]
301 mod trace_tests {
302 use {super::*, nom::sequence::tuple};
303
304 #[test]
305 fn test_tr_no_context() {
306 let mut parser = tr(
307 DEFAULT_TAG,
308 None,
309 "test_parser",
310 tag::<_, _, VerboseError<_>>("hello"),
311 );
312 let result = parser("hello world");
313 assert!(result.is_ok());
314
315 let trace = get_trace_for_tag(DEFAULT_TAG).unwrap();
316 assert!(trace.contains("test_parser"));
317 assert!(trace.contains("hello world"));
318 assert!(trace.contains("-> Ok"));
319 }
320
321 #[test]
322 fn test_tr_context() {
323 let mut parser = tr(
324 DEFAULT_TAG,
325 Some("context"),
326 "test_parser",
327 tag::<_, _, VerboseError<_>>("hello"),
328 );
329 let result = parser("hello world");
330 assert!(result.is_ok());
331
332 let trace = get_trace_for_tag(DEFAULT_TAG).unwrap();
333 assert!(trace.contains("test_parser"));
334 assert!(trace.contains("context"));
335 assert!(trace.contains("hello world"));
336 assert!(trace.contains("-> Ok"));
337 }
338
339 #[test]
340 fn test_nested_traces() {
341 fn parse_nested(input: &str) -> IResult<&str, (&str, &str)> {
342 tr(
343 DEFAULT_TAG,
344 None,
345 "outer",
346 tuple((
347 tr(DEFAULT_TAG, None, "inner_a", tag("a")),
348 tr(DEFAULT_TAG, None, "inner_b", tag("b")),
349 )),
350 )(input)
351 }
352
353 let traced_parser = parse_nested;
354 let result = traced_parser("ab");
355 assert!(result.is_ok());
356
357 let trace = get_trace_for_tag(DEFAULT_TAG).unwrap();
358 assert!(trace.contains("outer"));
359 assert!(trace.contains("inner_a"));
360 assert!(trace.contains("inner_b"));
361 }
362
363 #[test]
364 fn test_get_trace_for_tag() {
365 let mut parser = tr(
366 DEFAULT_TAG,
367 None,
368 "test_parser",
369 tag::<_, _, VerboseError<_>>("hello"),
370 );
371 let _ = parser("hello world");
372
373 let trace = get_trace_for_tag(DEFAULT_TAG).unwrap();
374 assert!(trace.contains("test_parser"));
375 assert!(trace.contains("hello world"));
376 }
377
378 #[test]
379 fn test_get_trace_for_nonexistent_tag() {
380 let trace = get_trace_for_tag("nonexistent");
381 assert!(trace.is_none());
382 }
383 }
384
385 #[cfg(feature = "trace-silencing")]
386 mod trace_silencing_tests {
387 use {super::*, nom::sequence::tuple};
388
389 #[test]
390 fn test_silence_tree() {
391 let mut parser = silence_tree(
392 DEFAULT_TAG,
393 Some("context"),
394 "silent_parser",
395 tag::<_, _, VerboseError<_>>("hello"),
396 );
397 let result = parser("hello world");
398 assert!(result.is_ok());
399
400 let trace = get_trace_for_tag(DEFAULT_TAG).unwrap();
401 assert!(!trace.contains("silent_parser"));
402 }
403
404 #[test]
405 fn test_silence_tree_with_nested_parsers() {
406 let mut outer_parser = tr(
407 DEFAULT_TAG,
408 None,
409 "outer_parser",
410 silence_tree(
411 DEFAULT_TAG,
412 None,
413 "inner_parser",
414 tr(
415 DEFAULT_TAG,
416 None,
417 "inner",
418 tag::<_, _, VerboseError<_>>("hello"),
419 ),
420 ),
421 );
422
423 let result = outer_parser("hello world");
424 assert!(result.is_ok());
425
426 let trace = get_trace_for_tag(DEFAULT_TAG).unwrap();
427 assert!(trace.contains("outer_parser"));
428 assert!(!trace.contains("inner_parser"));
429 }
430
431 #[test]
432 fn test_nested_silence_tree() {
433 #[allow(clippy::type_complexity)]
434 fn nested_parser(
435 input: &str,
436 ) -> IResult<&str, (&str, (&str, &str), &str), VerboseError<&str>> {
437 trace!(
438 "outer",
439 tuple((
440 trace!("first", tag::<_, _, VerboseError<&str>>("a")),
441 silence_tree!(
442 "silent",
443 tuple((
444 trace!("second", tag("b")),
445 silence_tree!("inner_silent", trace!("third", tag("c"))),
446 ))
447 ),
448 trace!("fourth", tag("d")),
449 ))
450 )(input)
451 }
452
453 let result = nested_parser("abcd");
455 assert!(result.is_ok());
456
457 let trace = get_trace!().unwrap();
459 println!("{}", trace);
460
461 assert!(trace.contains("outer"));
463 assert!(trace.contains("first"));
464 assert!(trace.contains("fourth"));
465
466 assert!(!trace.contains("silent"));
468 assert!(!trace.contains("second"));
469 assert!(!trace.contains("inner_silent"));
470 assert!(!trace.contains("third"));
471
472 assert_eq!(trace.matches("[outer]").count(), 2); assert_eq!(trace.matches("[first]").count(), 2); assert_eq!(trace.matches("[fourth]").count(), 2); assert!(!trace.contains("| | |"));
479
480 let trace_lines: Vec<&str> = trace.split('\n').collect();
482 assert!(
483 trace_lines
484 .iter()
485 .position(|&r| r.contains("first"))
486 .unwrap()
487 < trace_lines
488 .iter()
489 .position(|&r| r.contains("fourth"))
490 .unwrap()
491 );
492 }
493 }
494
495 #[cfg(all(feature = "trace", feature = "trace-context"))]
496 mod trace_context_tests {
497 use {
498 super::*,
499 nom::error::{ErrorKind, VerboseErrorKind},
500 };
501
502 #[test]
503 fn test_add_context_to_err() {
504 let mut parser = tr(
505 DEFAULT_TAG,
506 Some("context"),
507 "test_parser",
508 tag::<_, _, VerboseError<_>>("hello"),
509 );
510 let result = parser("world");
511
512 assert!(result.is_err());
513
514 if let Err(nom::Err::Error(e)) = result {
515 assert_eq!(e.errors.len(), 2);
516 assert_eq!(e.errors[1].1, VerboseErrorKind::Context("context"));
517 assert_eq!(e.errors[0].1, VerboseErrorKind::Nom(ErrorKind::Tag));
518 } else {
519 panic!("Expected Err(nom::Err::Error)");
520 }
521 }
522 }
523
524 #[cfg(not(feature = "trace"))]
525 mod no_trace_tests {
526 use {
527 super::*,
528 nom::error::{ErrorKind, VerboseErrorKind},
529 };
530
531 #[test]
532 fn test_tr_without_trace() {
533 let mut parser = tr(
534 DEFAULT_TAG,
535 Some("context"),
536 "test_parser",
537 tag::<_, _, VerboseError<_>>("hello"),
538 );
539 let result = parser("hello world");
540 assert!(result.is_ok());
541 assert_eq!(result, Ok((" world", "hello")));
542 }
543
544 #[test]
545 fn test_get_trace_for_tag_without_trace() {
546 assert!(get_trace_for_tag(DEFAULT_TAG).is_none());
547 }
548
549 #[cfg(feature = "trace-context")]
550 mod context_without_trace_tests {
551 use {
552 super::*,
553 nom::error::{ErrorKind, VerboseErrorKind},
554 };
555
556 #[test]
557 fn test_context_addition_without_trace() {
558 let mut parser = tr(
559 DEFAULT_TAG,
560 Some("context"),
561 "test_parser",
562 tag::<_, _, VerboseError<_>>("hello"),
563 );
564 let result = parser("world");
565
566 assert!(result.is_err());
567
568 if let Err(nom::Err::Error(e)) = result {
569 assert_eq!(e.errors.len(), 2);
570 assert_eq!(e.errors[1].1, VerboseErrorKind::Context("context"));
571 assert_eq!(e.errors[0].1, VerboseErrorKind::Nom(ErrorKind::Tag));
572 } else {
573 panic!("Expected Err(nom::Err::Error)");
574 }
575 }
576 }
577
578 #[cfg(not(feature = "trace-context"))]
579 #[test]
580 fn test_error_without_trace_and_context() {
581 let mut parser = tr(
582 DEFAULT_TAG,
583 Some("context"),
584 "test_parser",
585 tag::<_, _, VerboseError<_>>("hello"),
586 );
587 let result = parser("world");
588
589 assert!(result.is_err());
590
591 if let Err(nom::Err::Error(e)) = result {
592 println!("{:?}", e);
593 assert_eq!(e.errors.len(), 1);
594 assert_eq!(e.errors[0].1, VerboseErrorKind::Nom(ErrorKind::Tag));
595 } else {
596 panic!("Expected Err(nom::Err::Error)");
597 }
598 }
599 }
600}