1 | #![cfg_attr (feature = "debug" , allow(clippy::std_instead_of_core))] |
2 | |
3 | #[cfg (feature = "debug" )] |
4 | mod internals; |
5 | |
6 | use crate::error::ErrMode; |
7 | use crate::stream::Stream; |
8 | use crate::Parser; |
9 | |
10 | #[cfg (all(feature = "debug" , not(feature = "std" )))] |
11 | compile_error!("`debug` requires `std`" ); |
12 | |
13 | /// Trace the execution of the parser |
14 | /// |
15 | /// Note that [`Parser::context`] also provides high level trace information. |
16 | /// |
17 | /// See [tutorial][crate::_tutorial::chapter_8] for more details. |
18 | /// |
19 | /// # Example |
20 | /// |
21 | /// ```rust |
22 | /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; |
23 | /// # use winnow::token::take_while; |
24 | /// # use winnow::stream::AsChar; |
25 | /// # use winnow::prelude::*; |
26 | /// use winnow::combinator::trace; |
27 | /// |
28 | /// fn short_alpha<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { |
29 | /// trace("short_alpha" , |
30 | /// take_while(3..=6, AsChar::is_alpha) |
31 | /// ).parse_next(s) |
32 | /// } |
33 | /// |
34 | /// assert_eq!(short_alpha.parse_peek(b"latin123" ), Ok((&b"123" [..], &b"latin" [..]))); |
35 | /// assert_eq!(short_alpha.parse_peek(b"lengthy" ), Ok((&b"y" [..], &b"length" [..]))); |
36 | /// assert_eq!(short_alpha.parse_peek(b"latin" ), Ok((&b"" [..], &b"latin" [..]))); |
37 | /// assert_eq!(short_alpha.parse_peek(b"ed" ), Err(ErrMode::Backtrack(InputError::new(&b"ed" [..], ErrorKind::Slice)))); |
38 | /// assert_eq!(short_alpha.parse_peek(b"12345" ), Err(ErrMode::Backtrack(InputError::new(&b"12345" [..], ErrorKind::Slice)))); |
39 | /// ``` |
40 | #[cfg_attr (not(feature = "debug" ), allow(unused_variables))] |
41 | #[cfg_attr (not(feature = "debug" ), allow(unused_mut))] |
42 | #[cfg_attr (not(feature = "debug" ), inline(always))] |
43 | pub fn trace<I: Stream, O, E>( |
44 | name: impl crate::lib::std::fmt::Display, |
45 | parser: impl Parser<I, O, E>, |
46 | ) -> impl Parser<I, O, E> { |
47 | #[cfg (feature = "debug" )] |
48 | { |
49 | internals::Trace::new(parser, name) |
50 | } |
51 | #[cfg (not(feature = "debug" ))] |
52 | { |
53 | parser |
54 | } |
55 | } |
56 | |
57 | #[cfg_attr (not(feature = "debug" ), allow(unused_variables))] |
58 | pub(crate) fn trace_result<T, E>( |
59 | name: impl crate::lib::std::fmt::Display, |
60 | res: &Result<T, ErrMode<E>>, |
61 | ) { |
62 | #[cfg (feature = "debug" )] |
63 | { |
64 | let depth = internals::Depth::existing(); |
65 | let severity = internals::Severity::with_result(res); |
66 | internals::result(*depth, &name, severity); |
67 | } |
68 | } |
69 | |
70 | pub(crate) struct DisplayDebug<D>(pub(crate) D); |
71 | |
72 | impl<D: crate::lib::std::fmt::Debug> crate::lib::std::fmt::Display for DisplayDebug<D> { |
73 | fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { |
74 | write!(f, " {:?}" , self.0) |
75 | } |
76 | } |
77 | |
78 | #[test ] |
79 | #[cfg (feature = "std" )] |
80 | #[cfg_attr (miri, ignore)] |
81 | #[cfg (unix)] |
82 | #[cfg (feature = "debug" )] |
83 | fn example() { |
84 | use term_transcript::{test::TestConfig, ShellOptions}; |
85 | |
86 | let path = snapbox::cmd::compile_example("string" , ["--features=debug" ]).unwrap(); |
87 | |
88 | let current_dir = path.parent().unwrap(); |
89 | let cmd = path.file_name().unwrap(); |
90 | // HACK: term_transcript doesn't allow non-UTF8 paths |
91 | let cmd = format!("./ {}" , cmd.to_string_lossy()); |
92 | |
93 | TestConfig::new( |
94 | ShellOptions::default() |
95 | .with_current_dir(current_dir) |
96 | .with_env("CLICOLOR_FORCE" , "1" ), |
97 | ) |
98 | .test("assets/trace.svg" , [cmd.as_str()]); |
99 | } |
100 | |