neotoma/
lib.rs

1//! # Neotoma - Flexible Parser Combinator Library
2//!
3//! Neotoma is a Rust parsing library that provides a flexible, cached parser combinator framework.
4//! It's designed to parse structured data from any source implementing `Read + Seek` traits, with
5//! built-in memoization and backtracking capabilities.
6//!
7//! ## Key Features
8//!
9//! - **Composable parsers**: Build complex parsers from simple building blocks
10//! - **Automatic memoization**: Built-in caching prevents redundant parsing work
11//! - **Backtracking support**: Automatic position management for failed parses
12//! - **UTF-8 aware**: Comprehensive Unicode handling with ASCII optimization
13//! - **Thread-safe**: Uses efficient synchronization primitives from `parking_lot`
14//! - **Generic input**: Works with any `Read + Seek` source
15//!
16//! ## Quick Start
17//!
18//! ```rust
19//! use neotoma::prelude::*;
20//! use neotoma::{seq, oneof};
21//! use std::io::Cursor;
22//!
23//! // Build a parser for "hello" followed by optional whitespace and "world"
24//! let parser = seq![
25//!     Literal::from_str("hello"),
26//!     Optional::new(Utf8Class::whitespace()),
27//!     Literal::from_str("world")
28//! ];
29//!
30//! let input = Cursor::new(b"hello world");
31//! let mut source = Source::new(input);
32//!
33//! let result = parse(parser, &mut source);
34//! assert!(result.is_ok());
35//! ```
36//!
37//! ## Core Concepts
38//!
39//! ### Parser Trait
40//! All parsers implement the [`Parser`] trait, which provides:
41//! - `read()`: Implement your parsing logic here
42//! - `parse()`: Public API that handles caching and backtracking automatically
43//! - `id()`: Must be overridden for parameterized parsers to avoid cache conflicts
44//!
45//! ### Composition
46//! Build complex parsers using composition macros:
47//! - [`seq!`] for sequential parsing
48//! - [`oneof!`] for alternative choices
49//!
50//! ### Common Parser Types
51//! - [`Literal`]: Match exact byte sequences or strings
52//! - [`Class`]: Match character classes (byte-level)
53//! - [`Utf8Class`]: Match Unicode character classes
54//! - [`Repeat`]: Match repeated patterns with optional separators
55//! - [`Optional`]: Match zero or one occurrence
56//!
57//! ## Architecture
58//!
59//! The library uses a **Template Method Pattern** for the Parser trait, where `parse()`
60//! handles caching and backtracking, while implementations provide custom logic in `read()`.
61//! The caching system uses a **Strategy Pattern** allowing different cache implementations.
62
63pub mod cache;
64pub mod class;
65pub mod either;
66pub mod eof;
67pub mod grammar;
68pub mod literal;
69pub mod optional;
70pub mod parser;
71pub mod recursive;
72pub mod repeat;
73pub mod result;
74pub mod sequence;
75pub mod until;
76pub mod utf8class;
77pub mod utf8util;
78
79// Re-export the most commonly used types and functions at the root level
80// for better ergonomics
81
82// Core parser trait and utilities
83pub use parser::{Parser, Source, parse};
84
85// Common result types
86pub use result::{Error, ParseResult};
87
88// Main parser types (used in almost every parser combination)
89pub use class::Class;
90pub use literal::Literal;
91pub use utf8class::Utf8Class;
92
93// Common combinators
94pub use eof::EndOfFile;
95pub use optional::Optional;
96pub use recursive::Recursive;
97pub use repeat::Repeat;
98
99// Grammar parsing with named rules and recursion
100pub use grammar::GrammarParser;
101
102// Composition macros are already exported at crate root via #[macro_export]
103// seq! and oneof! are available directly
104
105// Prelude module for glob imports
106pub mod prelude {
107    //! Commonly used imports for parser development
108    //!
109    //! # Example
110    //!
111    //! ```rust
112    //! use neotoma::prelude::*;
113    //! use neotoma::{seq, oneof}; // Macros are at crate root
114    //!
115    //! // Now you have access to all the commonly used types
116    //! let parser = seq![
117    //!     Literal::from_str("hello"),
118    //!     Optional::new(Utf8Class::whitespace()),
119    //!     Literal::from_str("world")
120    //! ];
121    //! ```
122
123    pub use crate::{
124        Class,
125        // Combinators
126        EndOfFile,
127        Error,
128        // Grammar parsing
129        GrammarParser,
130        // Main parser types
131        Literal,
132        Optional,
133        ParseResult,
134        // Core traits and functions
135        Parser,
136        Recursive,
137        Repeat,
138        Source,
139        Utf8Class,
140        // Composition macros (re-exported from crate root)
141        // Note: seq! and oneof! macros are available directly, not as paths
142        // Cache trait for implementing custom parsers
143        cache::ParsingCache,
144        parse,
145    };
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151    use std::io::Cursor;
152
153    // Tests moved from tests/test_root_exports.rs
154
155    #[test]
156    fn test_root_level_exports_basic() {
157        // Test that we can use the types directly from root
158        let literal = Literal::from_str("test");
159
160        let cursor = Cursor::new(b"test");
161        let mut source = Source::new(cursor);
162
163        let result = parse(literal, &mut source);
164        assert!(result.is_ok());
165    }
166
167    #[test]
168    fn test_root_level_exports_with_macros() {
169        // Test using the seq! macro with root-level imports
170        let parser = seq![
171            Literal::from_str("Hello"),
172            Optional::new(Utf8Class::whitespace()),
173            Literal::from_str("World")
174        ];
175
176        let cursor = Cursor::new(b"Hello World");
177        let mut source = Source::new(cursor);
178
179        let result = parse(parser, &mut source);
180        assert!(result.is_ok());
181    }
182
183    #[test]
184    fn test_root_level_exports_oneof() {
185        // Test oneof! macro with root-level imports
186        let parser = oneof![Literal::from_str("hello"), Literal::from_str("world")];
187
188        let cursor = Cursor::new(b"hello");
189        let mut source = Source::new(cursor);
190
191        let result = parse(parser, &mut source);
192        assert!(result.is_ok());
193    }
194
195    #[test]
196    fn test_prelude_import() {
197        // Test the prelude module
198        use crate::prelude::*;
199
200        let parser = seq![Literal::from_str("test"), Optional::new(Utf8Class::alpha())];
201
202        let cursor = Cursor::new(b"testx");
203        let mut source = Source::new(cursor);
204
205        let result = parse(parser, &mut source);
206        assert!(result.is_ok());
207    }
208
209    #[test]
210    fn test_grammar_parser_exported() {
211        // Ensure GrammarParser is available at root level
212        let _parser = GrammarParser::new();
213    }
214}