nom-tracer 1.0.1

Extension of nom to trace parser execution
Documentation
// Copyright (c) Hexbee
// SPDX-License-Identifier: Apache-2.0

use {
    nom::{bytes::complete::tag, sequence::tuple, IResult},
    nom_tracer::*,
};

fn parse_ab(input: &str) -> IResult<&str, (&str, &str)> {
    tuple((tag("a"), tag("b")))(input)
}

#[test]
fn test_tr_function_always_works() {
    let result = tr("test_tag", Some("context"), "test_parser", parse_ab)("ab");
    assert!(result.is_ok());
    assert_eq!(result, Ok(("", ("a", "b"))));
}

#[cfg(feature = "trace")]
mod trace_tests {
    use super::*;

    #[test]
    fn test_simple_trace() {
        let result = trace!(parse_ab)("ab");
        assert!(result.is_ok());

        let trace = get_trace!().unwrap();
        assert!(trace.contains("test_simple_trace"));
        assert!(trace.contains("-> Ok"));
    }

    #[test]
    fn test_trace_with_tag() {
        let result = trace!(custom_tag, parse_ab)("ab");
        assert!(result.is_ok());

        let trace = get_trace!(custom_tag).unwrap();
        assert!(trace.contains("test_trace_with_tag"));
        assert!(trace.contains("-> Ok"));
    }

    #[test]
    fn test_trace_with_context() {
        let result = trace!("custom_context", parse_ab)("ab");
        assert!(result.is_ok());

        let trace = get_trace!().unwrap();
        assert!(trace.contains("test_trace_with_context"));
        assert!(trace.contains("custom_context"));
        assert!(trace.contains("-> Ok"));
    }

    #[test]
    fn test_activate_deactivate_reset() {
        activate_trace!();
        let result = trace!(parse_ab)("ab");
        assert!(result.is_ok());
        assert!(!get_trace!().unwrap().is_empty());

        deactivate_trace!();
        let result = trace!(parse_ab)("ab");
        assert!(result.is_ok());
        assert_eq!(
            get_trace!(),
            get_trace!(),
            "Trace should not change after deactivation"
        );

        activate_trace!();
        reset_trace!();
        assert!(
            get_trace!().unwrap().is_empty(),
            "Trace should be empty after reset"
        );

        let result = trace!(parse_ab)("ab");
        assert!(result.is_ok());
        assert!(
            !get_trace!().unwrap().is_empty(),
            "Trace should not be empty after new parsing"
        );
    }
}

// Tests for when trace-context feature is enabled
#[cfg(all(feature = "trace", feature = "trace-context"))]
mod trace_context_tests {
    use {
        super::*,
        nom::error::{ErrorKind, VerboseError, VerboseErrorKind},
    };

    #[test]
    fn test_context_addition() {
        let mut parser = tr(
            DEFAULT_TAG,
            Some("context"),
            "test_parser",
            tag::<_, _, VerboseError<_>>("hello"),
        );

        let result = parser("world");
        assert!(result.is_err());

        if let Err(nom::Err::Error(e)) = result {
            assert_eq!(e.errors.len(), 2);
            assert_eq!(e.errors[1].1, VerboseErrorKind::Context("context"));
            assert_eq!(e.errors[0].1, VerboseErrorKind::Nom(ErrorKind::Tag));
        } else {
            panic!("Expected Err(nom::Err::Error)");
        }
    }
}

// Tests for when trace-silencing feature is enabled
#[cfg(all(feature = "trace", feature = "trace-silencing"))]
mod trace_silencing_tests {
    use super::*;

    #[test]
    fn test_silence_tree() {
        fn outer_parser(input: &str) -> IResult<&str, (&str, &str)> {
            trace!("outer", tuple((tag("a"), silence_tree!("inner", tag("b")))))(input)
        }

        let result = outer_parser("ab");
        assert!(result.is_ok());

        let trace = get_trace!().unwrap();
        assert!(trace.contains("outer"));
        assert!(!trace.contains("inner"));
    }
}

// Tests for when trace is not enabled
#[cfg(not(feature = "trace"))]
mod no_trace_tests {
    use super::*;

    #[test]
    fn test_trace_macros_do_nothing() {
        let result = trace!(parse_ab)("ab");
        assert!(result.is_ok());

        // These should compile but do nothing
        activate_trace!();
        deactivate_trace!();
        reset_trace!();

        assert!(get_trace!().is_none());
    }

    #[cfg(feature = "trace-context")]
    #[test]
    fn test_context_addition_without_trace() {
        use nom::error::{ErrorKind, VerboseError};

        let mut parser = tr(
            DEFAULT_TAG,
            Some("context"),
            "test_parser",
            tag::<_, _, VerboseError<_>>("hello"),
        );

        let result = parser("world");
        assert!(result.is_err());

        if let Err(nom::Err::Error(e)) = result {
            assert_eq!(e.errors.len(), 2);
            assert_eq!(e.errors[1].1, VerboseErrorKind::Context("context"));
            assert_eq!(e.errors[0].1, VerboseErrorKind::Nom(ErrorKind::Tag));
        } else {
            panic!("Expected Err(nom::Err::Error)");
        }
    }
}