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 97 98
//! Parser execution tracing
//!
//! By default, nothing happens and tracing gets compiled away as a no-op. To enable tracing, use
//! `--features debug`.
//!
//! # Example
//!
//!![Trace output from string example](https://raw.githubusercontent.com/winnow-rs/winnow/main/assets/trace.svg "Example output")
#[cfg(feature = "debug")]
mod internals;
use crate::error::ErrMode;
use crate::stream::Stream;
use crate::Parser;
#[cfg(all(feature = "debug", not(feature = "std")))]
compile_error!("`debug` requires `std`");
/// Trace the execution of the parser
///
/// Note that [`Parser::context` also provides high level trace information.
///
/// See [`trace` module][self] 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::trace::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);
}
}
#[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()]);
}