summaryrefslogtreecommitdiffstats
path: root/vendor/winnow/src/trace
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/winnow/src/trace')
-rw-r--r--vendor/winnow/src/trace/internals.rs300
-rw-r--r--vendor/winnow/src/trace/mod.rs118
2 files changed, 418 insertions, 0 deletions
diff --git a/vendor/winnow/src/trace/internals.rs b/vendor/winnow/src/trace/internals.rs
new file mode 100644
index 000000000..2e91f69a8
--- /dev/null
+++ b/vendor/winnow/src/trace/internals.rs
@@ -0,0 +1,300 @@
+#![cfg(feature = "std")]
+
+use std::io::Write;
+
+use crate::error::ErrMode;
+use crate::stream::Stream;
+
+pub struct Depth {
+ depth: usize,
+ inc: bool,
+}
+
+impl Depth {
+ pub fn new() -> Self {
+ let depth = DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
+ let inc = true;
+ Self { depth, inc }
+ }
+
+ pub fn existing() -> Self {
+ let depth = DEPTH.load(std::sync::atomic::Ordering::SeqCst);
+ let inc = false;
+ Self { depth, inc }
+ }
+}
+
+impl Drop for Depth {
+ fn drop(&mut self) {
+ if self.inc {
+ let _ = DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
+ }
+ }
+}
+
+impl AsRef<usize> for Depth {
+ #[inline(always)]
+ fn as_ref(&self) -> &usize {
+ &self.depth
+ }
+}
+
+impl crate::lib::std::ops::Deref for Depth {
+ type Target = usize;
+
+ #[inline(always)]
+ fn deref(&self) -> &Self::Target {
+ &self.depth
+ }
+}
+
+static DEPTH: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
+
+pub enum Severity {
+ Success,
+ Backtrack,
+ Cut,
+ Incomplete,
+}
+
+impl Severity {
+ pub fn with_result<T, E>(result: &Result<T, ErrMode<E>>) -> Self {
+ match result {
+ Ok(_) => Self::Success,
+ Err(ErrMode::Backtrack(_)) => Self::Backtrack,
+ Err(ErrMode::Cut(_)) => Self::Cut,
+ Err(ErrMode::Incomplete(_)) => Self::Incomplete,
+ }
+ }
+}
+
+pub fn start<I: Stream>(
+ depth: usize,
+ name: &dyn crate::lib::std::fmt::Display,
+ count: usize,
+ input: &I,
+) {
+ let ansi_color = ansi_color();
+ let reset = if ansi_color {
+ anstyle::Reset.render().to_string()
+ } else {
+ "".to_owned()
+ };
+ let gutter_style = if ansi_color {
+ anstyle::Style::new().bold()
+ } else {
+ anstyle::Style::new()
+ }
+ .render();
+ let input_style = if ansi_color {
+ anstyle::Style::new().underline()
+ } else {
+ anstyle::Style::new()
+ }
+ .render();
+ let eof_style = if ansi_color {
+ anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Cyan.into()))
+ } else {
+ anstyle::Style::new()
+ }
+ .render();
+
+ let (call_width, input_width) = column_widths();
+
+ let count = if 0 < count {
+ format!(":{count}")
+ } else {
+ "".to_owned()
+ };
+ let call_column = format!("{:depth$}> {name}{count}", "");
+
+ let eof_offset = input.eof_offset();
+ let offset = input.offset_at(input_width).unwrap_or(eof_offset);
+ let (_, slice) = input.next_slice(offset);
+
+ // The debug version of `slice` might be wider, either due to rendering one byte as two nibbles or
+ // escaping in strings.
+ let mut debug_slice = format!("{:#?}", slice);
+ let (debug_slice, eof) = if let Some(debug_offset) = debug_slice
+ .char_indices()
+ .enumerate()
+ .find_map(|(pos, (offset, _))| (input_width <= pos).then(|| offset))
+ {
+ debug_slice.truncate(debug_offset);
+ let eof = "";
+ (debug_slice, eof)
+ } else {
+ let eof = if debug_slice.chars().count() < input_width {
+ "∅"
+ } else {
+ ""
+ };
+ (debug_slice, eof)
+ };
+
+ let writer = std::io::stderr();
+ let mut writer = writer.lock();
+ let _ = writeln!(writer, "{call_column:call_width$} {gutter_style}|{reset} {input_style}{debug_slice}{eof_style}{eof}{reset}");
+}
+
+pub fn end(
+ depth: usize,
+ name: &dyn crate::lib::std::fmt::Display,
+ count: usize,
+ consumed: Option<usize>,
+ severity: Severity,
+) {
+ let ansi_color = ansi_color();
+ let reset = if ansi_color {
+ anstyle::Reset.render().to_string()
+ } else {
+ "".to_owned()
+ };
+ let gutter_style = if ansi_color {
+ anstyle::Style::new().bold()
+ } else {
+ anstyle::Style::new()
+ }
+ .render();
+
+ let (call_width, _) = column_widths();
+
+ let count = if 0 < count {
+ format!(":{count}")
+ } else {
+ "".to_owned()
+ };
+ let call_column = format!("{:depth$}< {name}{count}", "");
+
+ let (mut status_style, status) = match severity {
+ Severity::Success => {
+ let style = anstyle::Style::new()
+ .fg_color(Some(anstyle::AnsiColor::Green.into()))
+ .render();
+ let status = format!("+{}", consumed.unwrap_or_default());
+ (style, status)
+ }
+ Severity::Backtrack => (
+ anstyle::Style::new()
+ .fg_color(Some(anstyle::AnsiColor::Yellow.into()))
+ .render(),
+ "backtrack".to_owned(),
+ ),
+ Severity::Cut => (
+ anstyle::Style::new()
+ .fg_color(Some(anstyle::AnsiColor::Red.into()))
+ .render(),
+ "cut".to_owned(),
+ ),
+ Severity::Incomplete => (
+ anstyle::Style::new()
+ .fg_color(Some(anstyle::AnsiColor::Red.into()))
+ .render(),
+ "incomplete".to_owned(),
+ ),
+ };
+ if !ansi_color {
+ status_style = anstyle::Style::new().render();
+ }
+
+ let writer = std::io::stderr();
+ let mut writer = writer.lock();
+ let _ = writeln!(
+ writer,
+ "{status_style}{call_column:call_width$}{reset} {gutter_style}|{reset} {status_style}{status}{reset}"
+ );
+}
+
+pub fn result(depth: usize, name: &dyn crate::lib::std::fmt::Display, severity: Severity) {
+ let ansi_color = ansi_color();
+ let reset = if ansi_color {
+ anstyle::Reset.render().to_string()
+ } else {
+ "".to_owned()
+ };
+ let gutter_style = if ansi_color {
+ anstyle::Style::new().bold()
+ } else {
+ anstyle::Style::new()
+ }
+ .render();
+
+ let (call_width, _) = column_widths();
+
+ let call_column = format!("{:depth$}| {name}", "");
+
+ let (mut status_style, status) = match severity {
+ Severity::Success => (
+ anstyle::Style::new()
+ .fg_color(Some(anstyle::AnsiColor::Green.into()))
+ .render(),
+ "",
+ ),
+ Severity::Backtrack => (
+ anstyle::Style::new()
+ .fg_color(Some(anstyle::AnsiColor::Yellow.into()))
+ .render(),
+ "backtrack",
+ ),
+ Severity::Cut => (
+ anstyle::Style::new()
+ .fg_color(Some(anstyle::AnsiColor::Red.into()))
+ .render(),
+ "cut",
+ ),
+ Severity::Incomplete => (
+ anstyle::Style::new()
+ .fg_color(Some(anstyle::AnsiColor::Red.into()))
+ .render(),
+ "incomplete",
+ ),
+ };
+ if !ansi_color {
+ status_style = anstyle::Style::new().render();
+ }
+
+ let writer = std::io::stderr();
+ let mut writer = writer.lock();
+ let _ = writeln!(
+ writer,
+ "{status_style}{call_column:call_width$}{reset} {gutter_style}|{reset} {status_style}{status}{reset}"
+ );
+}
+
+fn ansi_color() -> bool {
+ concolor::get(concolor::Stream::Stderr).ansi_color()
+}
+
+fn column_widths() -> (usize, usize) {
+ let term_width = term_width();
+
+ let min_call_width = 40;
+ let min_input_width = 20;
+ let decor_width = 3;
+ let extra_width = term_width
+ .checked_sub(min_call_width + min_input_width + decor_width)
+ .unwrap_or_default();
+ let call_width = min_call_width + 2 * extra_width / 3;
+ let input_width = min_input_width + extra_width / 3;
+
+ (call_width, input_width)
+}
+
+fn term_width() -> usize {
+ columns_env().or_else(query_width).unwrap_or(80)
+}
+
+fn query_width() -> Option<usize> {
+ use is_terminal::IsTerminal;
+ if std::io::stderr().is_terminal() {
+ terminal_size::terminal_size().map(|(w, _h)| w.0.into())
+ } else {
+ None
+ }
+}
+
+fn columns_env() -> Option<usize> {
+ std::env::var("COLUMNS")
+ .ok()
+ .and_then(|c| c.parse::<usize>().ok())
+}
diff --git a/vendor/winnow/src/trace/mod.rs b/vendor/winnow/src/trace/mod.rs
new file mode 100644
index 000000000..59dafba8e
--- /dev/null
+++ b/vendor/winnow/src/trace/mod.rs
@@ -0,0 +1,118 @@
+//! 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::{Error, ErrorKind}, error::Needed, IResult};
+/// # use winnow::bytes::take_while_m_n;
+/// # use winnow::stream::AsChar;
+/// # use winnow::prelude::*;
+/// use winnow::trace::trace;
+///
+/// fn short_alpha(s: &[u8]) -> IResult<&[u8], &[u8]> {
+/// trace("short_alpha",
+/// take_while_m_n(3, 6, AsChar::is_alpha)
+/// ).parse_next(s)
+/// }
+///
+/// assert_eq!(short_alpha(b"latin123"), Ok((&b"123"[..], &b"latin"[..])));
+/// assert_eq!(short_alpha(b"lengthy"), Ok((&b"y"[..], &b"length"[..])));
+/// assert_eq!(short_alpha(b"latin"), Ok((&b""[..], &b"latin"[..])));
+/// assert_eq!(short_alpha(b"ed"), Err(ErrMode::Backtrack(Error::new(&b"ed"[..], ErrorKind::Slice))));
+/// assert_eq!(short_alpha(b"12345"), Err(ErrMode::Backtrack(Error::new(&b"12345"[..], ErrorKind::Slice))));
+/// ```
+#[cfg_attr(not(feature = "debug"), allow(unused_variables))]
+#[cfg_attr(not(feature = "debug"), allow(unused_mut))]
+pub fn trace<I: Stream, O, E>(
+ name: impl crate::lib::std::fmt::Display,
+ mut parser: impl Parser<I, O, E>,
+) -> impl Parser<I, O, E> {
+ #[cfg(feature = "debug")]
+ {
+ let mut call_count = 0;
+ move |i: I| {
+ let depth = internals::Depth::new();
+ let original = i.clone();
+ internals::start(*depth, &name, call_count, &original);
+
+ let res = parser.parse_next(i);
+
+ let consumed = res.as_ref().ok().map(|(i, _)| {
+ if i.eof_offset() == 0 {
+ // Sometimes, an unrelated empty string is returned which can break `offset_to`
+ original.eof_offset()
+ } else {
+ original.offset_to(i)
+ }
+ });
+ let severity = internals::Severity::with_result(&res);
+ internals::end(*depth, &name, call_count, consumed, severity);
+ call_count += 1;
+
+ res
+ }
+ }
+ #[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()]);
+}