neotoma 0.1.1

A flexible, cached parser combinator framework for Rust.
Documentation
//! # Neotoma - Flexible Parser Combinator Library
//!
//! Neotoma is a Rust parsing library that provides a flexible, cached parser combinator framework.
//! It's designed to parse structured data from any source implementing `Read + Seek` traits, with
//! built-in memoization and backtracking capabilities.
//!
//! ## Key Features
//!
//! - **Composable parsers**: Build complex parsers from simple building blocks
//! - **Automatic memoization**: Built-in caching prevents redundant parsing work
//! - **Backtracking support**: Automatic position management for failed parses
//! - **UTF-8 aware**: Comprehensive Unicode handling with ASCII optimization
//! - **Thread-safe**: Uses efficient synchronization primitives from `parking_lot`
//! - **Generic input**: Works with any `Read + Seek` source
//!
//! ## Quick Start
//!
//! ```rust
//! use neotoma::prelude::*;
//! use neotoma::{seq, oneof};
//! use std::io::Cursor;
//!
//! // Build a parser for "hello" followed by optional whitespace and "world"
//! let parser = seq![
//!     Literal::from_str("hello"),
//!     Optional::new(Utf8Class::whitespace()),
//!     Literal::from_str("world")
//! ];
//!
//! let input = Cursor::new(b"hello world");
//! let mut source = Source::new(input);
//!
//! let result = parse(parser, &mut source);
//! assert!(result.is_ok());
//! ```
//!
//! ## Core Concepts
//!
//! ### Parser Trait
//! All parsers implement the [`Parser`] trait, which provides:
//! - `read()`: Implement your parsing logic here
//! - `parse()`: Public API that handles caching and backtracking automatically
//! - `id()`: Must be overridden for parameterized parsers to avoid cache conflicts
//!
//! ### Composition
//! Build complex parsers using composition macros:
//! - [`seq!`] for sequential parsing
//! - [`oneof!`] for alternative choices
//!
//! ### Common Parser Types
//! - [`Literal`]: Match exact byte sequences or strings
//! - [`Class`]: Match character classes (byte-level)
//! - [`Utf8Class`]: Match Unicode character classes
//! - [`Repeat`]: Match repeated patterns with optional separators
//! - [`Optional`]: Match zero or one occurrence
//!
//! ## Architecture
//!
//! The library uses a **Template Method Pattern** for the Parser trait, where `parse()`
//! handles caching and backtracking, while implementations provide custom logic in `read()`.
//! The caching system uses a **Strategy Pattern** allowing different cache implementations.

pub mod cache;
pub mod class;
pub mod either;
pub mod eof;
pub mod grammar;
pub mod literal;
pub mod optional;
pub mod parser;
pub mod recursive;
pub mod repeat;
pub mod result;
pub mod sequence;
pub mod until;
pub mod utf8class;
pub mod utf8util;

// Re-export the most commonly used types and functions at the root level
// for better ergonomics

// Core parser trait and utilities
pub use parser::{Parser, Source, parse};

// Common result types
pub use result::{Error, ParseResult};

// Main parser types (used in almost every parser combination)
pub use class::Class;
pub use literal::Literal;
pub use utf8class::Utf8Class;

// Common combinators
pub use eof::EndOfFile;
pub use optional::Optional;
pub use recursive::Recursive;
pub use repeat::Repeat;

// Grammar parsing with named rules and recursion
pub use grammar::GrammarParser;

// Composition macros are already exported at crate root via #[macro_export]
// seq! and oneof! are available directly

// Prelude module for glob imports
pub mod prelude {
    //! Commonly used imports for parser development
    //!
    //! # Example
    //!
    //! ```rust
    //! use neotoma::prelude::*;
    //! use neotoma::{seq, oneof}; // Macros are at crate root
    //!
    //! // Now you have access to all the commonly used types
    //! let parser = seq![
    //!     Literal::from_str("hello"),
    //!     Optional::new(Utf8Class::whitespace()),
    //!     Literal::from_str("world")
    //! ];
    //! ```

    pub use crate::{
        Class,
        // Combinators
        EndOfFile,
        Error,
        // Grammar parsing
        GrammarParser,
        // Main parser types
        Literal,
        Optional,
        ParseResult,
        // Core traits and functions
        Parser,
        Recursive,
        Repeat,
        Source,
        Utf8Class,
        // Composition macros (re-exported from crate root)
        // Note: seq! and oneof! macros are available directly, not as paths
        // Cache trait for implementing custom parsers
        cache::ParsingCache,
        parse,
    };
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;

    // Tests moved from tests/test_root_exports.rs

    #[test]
    fn test_root_level_exports_basic() {
        // Test that we can use the types directly from root
        let literal = Literal::from_str("test");

        let cursor = Cursor::new(b"test");
        let mut source = Source::new(cursor);

        let result = parse(literal, &mut source);
        assert!(result.is_ok());
    }

    #[test]
    fn test_root_level_exports_with_macros() {
        // Test using the seq! macro with root-level imports
        let parser = seq![
            Literal::from_str("Hello"),
            Optional::new(Utf8Class::whitespace()),
            Literal::from_str("World")
        ];

        let cursor = Cursor::new(b"Hello World");
        let mut source = Source::new(cursor);

        let result = parse(parser, &mut source);
        assert!(result.is_ok());
    }

    #[test]
    fn test_root_level_exports_oneof() {
        // Test oneof! macro with root-level imports
        let parser = oneof![Literal::from_str("hello"), Literal::from_str("world")];

        let cursor = Cursor::new(b"hello");
        let mut source = Source::new(cursor);

        let result = parse(parser, &mut source);
        assert!(result.is_ok());
    }

    #[test]
    fn test_prelude_import() {
        // Test the prelude module
        use crate::prelude::*;

        let parser = seq![Literal::from_str("test"), Optional::new(Utf8Class::alpha())];

        let cursor = Cursor::new(b"testx");
        let mut source = Source::new(cursor);

        let result = parse(parser, &mut source);
        assert!(result.is_ok());
    }

    #[test]
    fn test_grammar_parser_exported() {
        // Ensure GrammarParser is available at root level
        let _parser = GrammarParser::new();
    }
}