winnow/combinator/debug/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#![cfg_attr(feature = "debug", allow(clippy::std_instead_of_core))]

#[cfg(feature = "debug")]
mod internals;

use crate::error::ErrMode;
use crate::stream::Stream;
use crate::Parser;

/// Trace the execution of the parser
///
/// Note that [`Parser::context`] also provides high level trace information.
///
/// See [tutorial][crate::_tutorial::chapter_8] for more details.
///
/// # Example
///
/// ```rust
/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
/// # use winnow::token::take_while;
/// # use winnow::stream::AsChar;
/// # use winnow::prelude::*;
/// use winnow::combinator::trace;
///
/// fn short_alpha<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> {
///   trace("short_alpha",
///     take_while(3..=6, AsChar::is_alpha)
///   ).parse_next(s)
/// }
///
/// assert_eq!(short_alpha.parse_peek(b"latin123"), Ok((&b"123"[..], &b"latin"[..])));
/// assert_eq!(short_alpha.parse_peek(b"lengthy"), Ok((&b"y"[..], &b"length"[..])));
/// assert_eq!(short_alpha.parse_peek(b"latin"), Ok((&b""[..], &b"latin"[..])));
/// assert_eq!(short_alpha.parse_peek(b"ed"), Err(ErrMode::Backtrack(InputError::new(&b"ed"[..], ErrorKind::Slice))));
/// assert_eq!(short_alpha.parse_peek(b"12345"), Err(ErrMode::Backtrack(InputError::new(&b"12345"[..], ErrorKind::Slice))));
/// ```
#[cfg_attr(not(feature = "debug"), allow(unused_variables))]
#[cfg_attr(not(feature = "debug"), allow(unused_mut))]
#[cfg_attr(not(feature = "debug"), inline(always))]
pub fn trace<I: Stream, O, E>(
    name: impl crate::lib::std::fmt::Display,
    parser: impl Parser<I, O, E>,
) -> impl Parser<I, O, E> {
    #[cfg(feature = "debug")]
    {
        internals::Trace::new(parser, name)
    }
    #[cfg(not(feature = "debug"))]
    {
        parser
    }
}

#[cfg_attr(not(feature = "debug"), allow(unused_variables))]
pub(crate) fn trace_result<T, E>(
    name: impl crate::lib::std::fmt::Display,
    res: &Result<T, ErrMode<E>>,
) {
    #[cfg(feature = "debug")]
    {
        let depth = internals::Depth::existing();
        let severity = internals::Severity::with_result(res);
        internals::result(*depth, &name, severity);
    }
}

pub(crate) struct DisplayDebug<D>(pub(crate) D);

impl<D: crate::lib::std::fmt::Debug> crate::lib::std::fmt::Display for DisplayDebug<D> {
    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
        write!(f, "{:?}", self.0)
    }
}

#[test]
#[cfg(feature = "std")]
#[cfg_attr(miri, ignore)]
#[cfg(unix)]
#[cfg(feature = "debug")]
fn example() {
    use term_transcript::{test::TestConfig, ShellOptions};

    let path = snapbox::cmd::compile_example("string", ["--features=debug"]).unwrap();

    let current_dir = path.parent().unwrap();
    let cmd = path.file_name().unwrap();
    // HACK: term_transcript doesn't allow non-UTF8 paths
    let cmd = format!("./{}", cmd.to_string_lossy());

    TestConfig::new(
        ShellOptions::default()
            .with_current_dir(current_dir)
            .with_env("CLICOLOR_FORCE", "1"),
    )
    .test("assets/trace.svg", [cmd.as_str()]);
}