# nom-tracer [![Crates.io](https://img.shields.io/crates/v/nom-tracer.svg)](https://crates.io/crates/nom-tracer) [![Documentation](https://docs.rs/nom-tracer/badge.svg)](https://docs.rs/nom-tracer) [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) `nom-tracer` is a powerful and flexible tracing utility for the [nom](https://github.com/Geal/nom) parser combinator library. It allows you to easily trace the execution of your parsers, providing invaluable insights for debugging and optimization.
## Contents - [Features](#features) - [Performance](#performance) - [Quick Start](#quick-start) - [Macros](#macros) - [trace!](#trace) - [silence_tree!](#silence_tree) - [activate_trace! and deactivate_trace!](#activate_trace-and-deactivate_trace) - [reset_trace!](#reset_trace) - [get_trace!](#get_trace) - [print_trace!](#print_trace) - [set_max_level!](#set_max_level) - [Cargo Features](#cargo-features) - [Context Information](#context-information) - [Contributing](#contributing) - [License](#license) ## Features - 🔍 Trace parser execution with minimal code changes - 🚀 **Zero overhead when disabled** - compile out all tracing code in release builds - 🎨 Colorized output for easy reading (optional) - 🏷️ Support for multiple trace tags to organize parser traces - 📊 Hierarchical view of parser execution - 🤫 Silence subtrees to reduce noise in well-tested parts of your parser - 🔧 Configurable via Cargo features ## Performance One of the key advantages of `nom-tracer` is its minimal performance impact: - **When disabled**: The tracing code is completely compiled out, resulting in **virtually zero overhead**. Your parsers will run at full speed in production builds. - **When enabled**: The tracing functionality is designed to be as lightweight as possible, allowing for detailed insights with minimal performance cost during development and debugging. ## Quick Start Add `nom-tracer` to your `Cargo.toml`: ```toml [dependencies] nom-tracer = "0.2" ``` Then, use the `trace!` macro to wrap your parsers: ```rust use nom_tracer::trace; use nom::bytes::complete::tag; use nom::IResult; fn parse_hello(input: &str) -> IResult<&str, &str> { trace!(tag("hello"))(input) } fn parse_world(input: &str) -> IResult<&str, &str> { trace!(tag("world"))(input) } fn main() { let result = parse_hello("hello world"); println!("Parse result: {:?}", result); println!("Trace:\n{}", nom_tracer::get_trace!()); } ``` For production builds, you can disable all tracing features to ensure zero overhead: ```toml [dependencies] nom-tracer = { version = "0.2", default-features = false } ``` For more detailed examples showcasing various features of `nom-tracer`, check out the [`examples`](https://github.com/hexbee-net/nom-tracer/tree/main/examples) folder in the root of the repository. These examples demonstrate real-world usage scenarios and can help you get started with more advanced tracing techniques. ## Macros `nom-tracer` provides several macros to make tracing easier and more flexible. Here's a detailed explanation of each macro: ### trace! The `trace!` macro is the primary way to add tracing to your nom parsers. It wraps a parser with tracing functionality, recording its execution path and results. You can use `trace!` in several ways: 1. `trace!(parser)`: Uses the default tag and no context. 2. `trace!(tag, parser)`: Uses a custom tag and no context. 3. `trace!("context", parser)`: Uses the default tag and a custom context. 4. `trace!(tag, "context", parser)`: Uses a custom tag and a custom context. Here's an example that demonstrates these different usage patterns: ```rust use nom_tracer::trace; use nom::sequence::tuple; use nom::character::complete::{alpha1, char, digit1}; use nom::IResult; fn parse_person(input: &str) -> IResult<&str, (&str, char, &str)> { trace!( person_parser, "Parsing person: name,separator,age", tuple(( trace!(name_parser, "Parsing name", alpha1), trace!(char(',')), trace!(age_parser, "Parsing age", digit1) )) )(input) } ``` The `trace!` macro is designed to be flexible and easy to use in various scenarios. It automatically captures the function name where it's used, reducing boilerplate code. When compiling with the `trace` feature disabled, all `trace!` macros effectively disappear, leaving no runtime overhead. ### silence_tree! The `silence_tree!` macro allows you to silence tracing for a subtree of parsers. This is useful for reducing noise in well-tested or less interesting parts of your parser. You can use `silence_tree!` in several ways: 1. `silence_tree!(parser)`: Silences the default tag. 2. `silence_tree!(tag, parser)`: Silences a specific tag. 3. `silence_tree!("context", parser)`: Silences the default tag with a context. 4. `silence_tree!(tag, "context", parser)`: Silences a specific tag with a context. Here's an example: ```rust use nom_tracer::{trace, silence_tree}; use nom::sequence::tuple; use nom::character::complete::{alpha1, char, digit1}; use nom::IResult; fn parse_person(input: &str) -> IResult<&str, (&str, char, &str)> { trace!( person_parser, "Parsing person", tuple(( trace!(alpha1), trace!(char(',')), silence_tree!("Silenced age parsing", digit1) )) )(input) } ``` Use `silence_tree!` for parts of your parser that are well-tested or when you want to focus on specific areas of your parser. Silenced subtrees still execute normally but don't generate trace output. The `silence_tree!` macro is only available when the `trace-silencing` feature is enabled. ### activate_trace! and deactivate_trace! These macros allow you to dynamically activate or deactivate tracing for either the default tag or a specified tag. ```rust use nom_tracer::{activate_trace, deactivate_trace}; // Activate/deactivate tracing for the default tag activate_trace!(); deactivate_trace!(); // Activate/deactivate tracing for a custom tag activate_trace!(my_custom_tag); deactivate_trace!(my_custom_tag); ``` These macros are useful for selectively enabling or disabling tracing in different parts of your application. You can use them to control tracing at runtime based on conditions or user input. ### reset_trace! This macro resets the trace for either the default tag or a specified tag, clearing all recorded events. ```rust use nom_tracer::reset_trace; // Reset trace for the default tag reset_trace!(); // Reset trace for a custom tag reset_trace!(my_custom_tag); ``` `reset_trace!` is handy when you want to clear previous trace data and start fresh. It can be particularly useful in long-running applications or test suites where you want to reset the trace between different parsing operations. ### get_trace! The `get_trace!` macro retrieves the trace for either the default tag or a specified tag. ```rust use nom_tracer::{trace, get_trace}; use nom::bytes::complete::tag; fn main() { let _ = trace!(tag("hello"))("hello world"); let default_trace = get_trace!(); // Gets trace for default tag let _ = trace!(my_tag, tag("hello"))("hello world"); let my_tag_trace = get_trace!(my_tag); // Gets trace for "my_tag" println!("Default trace:\n{}", default_trace.unwrap_or_default()); println!("My tag trace:\n{}", my_tag_trace.unwrap_or_default()); } ``` `get_trace!` returns an `Option