//! Core parser traits and utilities. //! //! This module contains the fundamental [`Parser`] trait that all parsers implement, //! along with the [`Source`] wrapper for managing input streams with position tracking //! and backtracking support. //! //! The [`Parser`] trait uses a Template Method Pattern where the `parse()` method //! handles caching and backtracking automatically, while implementations provide //! custom parsing logic in the `read()` method. //! //! The module also provides comprehensive blanket implementations for smart pointers //! like `Box`, `Arc`, `Rc`, and synchronization primitives, enabling //! flexible parser composition and thread-safe usage patterns. use std::cell::RefCell; use std::hash::{DefaultHasher, Hash, Hasher}; use std::io::{Read, Seek}; use std::rc::{Rc, Weak as RcWeak}; use std::sync::{Arc, Mutex as StdMutex, RwLock as StdRwLock, Weak as ArcWeak}; use std::any::{Any, TypeId}; use parking_lot::{Mutex, RwLock}; use crate::{ cache::{BasicCache, ParsingCache}, result::{Error, ParseResult}, }; pub trait Parsable: Read + Seek {} impl Parsable for T where T: Read + Seek {} /// Encapsulates a [`std::io::Read`] + [`std::io::Seek`] as a source /// of bytes to parse from. /// /// Note that while anything that has the required traits will work, /// using a [`std::io::BufReader`] or [`std::io::Cursor`] will usually /// be the most efficient choice. Seek capabilities are used heavily /// while parsing. pub struct Source { source: S, stack: Vec, index: u64, } impl Source where S: Parsable, { /// Create a new Source wrapping a Read + Seek. pub fn new(mut source: S) -> Source { let index = source.stream_position().unwrap_or(0); Source { source, stack: Vec::with_capacity(20), index, } } /// Store the current location. Each push should be paired with /// either a pop (to backtrack to the pushed location) or a commit /// (to assert that the pushed location is no longer needed) pub fn push(&mut self) { self.stack.push(self.index); } /// Return to the previously pushed location pub fn pop(&mut self) { self.index = self.stack.pop().unwrap_or(0); } /// Discard the previously pushed location pub fn commit(&mut self) { self.stack.pop(); } /// Retrieve the next byte from the source pub fn peek1(&mut self) -> Result { let mut buf = [0; 1]; self.source.seek(std::io::SeekFrom::Start(self.index))?; if let Err(err) = self.source.read_exact(&mut buf) { if err.kind() == std::io::ErrorKind::UnexpectedEof { return Err(Error::NoMatch); } return Err(Error::from(err)); } Ok(buf[0]) } /// Retrieve the next `bytes` bytes from the source pub fn peek(&mut self, bytes: usize) -> Result, Error> { let mut buf = vec![0; bytes]; self.source.seek(std::io::SeekFrom::Start(self.index))?; if let Err(err) = self.source.read_exact(&mut buf) { if err.kind() == std::io::ErrorKind::UnexpectedEof { return Err(Error::NoMatch); } return Err(Error::from(err)); } Ok(buf) } /// Move the current position in the source forward by `bytes` bytes. pub fn advance(&mut self, bytes: usize) { self.index += bytes as u64; } /// Retrieve the next `bytes` bytes from the source, and move the /// current position forward by the same number of bytes. pub fn read(&mut self, bytes: usize) -> Result, Error> { let ret = self.peek(bytes)?; self.advance(bytes); Ok(ret) } } pub trait Parser where Self: Any, { type Output: Any + Clone; /// This is the function you implement in order to make a type Parsable fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable; /// Identifies the specific parser. For parsers which are not /// parameterized, the default TypeId-based implementation is /// sufficient. However, if the parser is contructed with /// parameters, it may need to have a different id for each /// combination of parameters in order to avoid false positives in /// the cache. For example, the Literal parser needs to override /// this so that parsers for different literals are not conflated /// in the cache. /// /// It's recommended that when overridden, the return value should /// be generated with [DefaultHasher], and `TypeId::of()` /// should be the first thing added to the hash, followed by /// whatever other information is needed to differentiate /// instances of the type. fn id(&self) -> u64 { let mut hasher = DefaultHasher::new(); TypeId::of::().hash(&mut hasher); hasher.finish() } /// This is the function you call to try parsing the type from the /// source. It is a wrapper which does necessary book-keeping /// around calls to the `read` function. fn parse( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable, { let parser_id = self.id(); let before = source.index; if let Some((cached_result, after)) = cache.lookup::(parser_id, before) { source.index = after; return Ok(cached_result); } source.push(); match self.read(source, cache, context) { Ok(val) => { source.commit(); // Store result in cache cache.record::(parser_id, before, val.clone(), source.index); Ok(val) } Err(err) => match err { Error::NoMatch => { source.pop(); Err(err) } _ => Err(err), }, } } } /// Global entry point for parsing a grammar pub fn parse(parser: R, source: &mut Source) -> Result where R: Parser, S: Parsable, { let mut cache = BasicCache::new(); let mut context = (); parser.parse(source, &mut cache, &mut context) } /// Global entry point for parsing a grammar, if you want to specify a cache pub fn parse_with_cache( parser: R, source: &mut Source, cache: &mut impl ParsingCache, ) -> Result where R: Parser, S: Parsable, { let mut context = (); parser.parse(source, cache, &mut context) } /// Global entry point for parsing a grammar with context pub fn parse_with_context( parser: R, source: &mut Source, context: &mut Ctx, ) -> Result where R: Parser, S: Parsable, { let mut cache = BasicCache::new(); parser.parse(source, &mut cache, context) } /// Global entry point for parsing a grammar with both cache and context pub fn parse_with_cache_and_context( parser: R, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> Result where R: Parser, S: Parsable, { parser.parse(source, cache, context) } /// () is a Parser which always matches after consuming zero bytes. impl Parser for () { type Output = (); fn read( &self, _source: &mut Source, _cache: &mut impl ParsingCache, _context: &mut Ctx, ) -> ParseResult where S: Parsable, { Ok(()) } } /// Blanket implementation for `Box where T: Parser` /// This allows boxed parsers to work correctly with the parsing system impl Parser for Box where T: Parser, { type Output = T::Output; fn id(&self) -> u64 { self.as_ref().id() } fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable, { self.as_ref().read(source, cache, context) } } /// Blanket implementation for `Arc where T: Parser` /// This allows shared parsers to work correctly with the parsing system impl Parser for Arc where T: Parser, { type Output = T::Output; fn id(&self) -> u64 { self.as_ref().id() } fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable, { self.as_ref().read(source, cache, context) } } /// Blanket implementation for `Rc where T: Parser` /// This allows reference-counted parsers to work correctly with the parsing system impl Parser for Rc where T: Parser, { type Output = T::Output; fn id(&self) -> u64 { self.as_ref().id() } fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable, { self.as_ref().read(source, cache, context) } } /// Blanket implementation for `Mutex where T: Parser` /// This allows mutex-protected parsers to work correctly with the parsing system impl Parser for Mutex where T: Parser, { type Output = T::Output; fn id(&self) -> u64 { self.lock().id() } fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable, { self.lock().read(source, cache, context) } } /// Blanket implementation for `RwLock where T: Parser` /// This allows read-write locked parsers to work correctly with the parsing system impl Parser for RwLock where T: Parser, { type Output = T::Output; fn id(&self) -> u64 { self.read().id() } fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable, { self.read().read(source, cache, context) } } /// Blanket implementation for `RefCell where T: Parser` /// This allows ref-cell protected parsers to work correctly with the parsing system impl Parser for RefCell where T: Parser, { type Output = T::Output; fn id(&self) -> u64 { self.borrow().id() } fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable, { self.borrow().read(source, cache, context) } } /// Blanket implementation for `std::sync::Mutex where T: Parser` /// This allows standard library mutex-protected parsers to work correctly with the parsing system /// Panics if the mutex is poisoned impl Parser for StdMutex where T: Parser, { type Output = T::Output; fn id(&self) -> u64 { self.lock().expect("Mutex poisoned").id() } fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable, { self.lock() .expect("Mutex poisoned") .read(source, cache, context) } } /// Blanket implementation for `std::sync::RwLock where T: Parser` /// This allows standard library read-write locked parsers to work correctly with the parsing system /// Panics if the RwLock is poisoned impl Parser for StdRwLock where T: Parser, { type Output = T::Output; fn id(&self) -> u64 { self.read().expect("RwLock poisoned").id() } fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable, { self.read() .expect("RwLock poisoned") .read(source, cache, context) } } /// Blanket implementation for `ArcWeak where T: Parser` /// This allows weak references to shared parsers impl Parser for ArcWeak where T: Parser, { type Output = T::Output; fn id(&self) -> u64 { // For weak references, we need to upgrade to get the id // If the upgrade fails, we'll generate a default id based on the type if let Some(strong) = self.upgrade() { strong.as_ref().id() } else { // If the reference is dead, generate a consistent id based on the type let mut hasher = DefaultHasher::new(); TypeId::of::>().hash(&mut hasher); hasher.finish() } } fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable, { if let Some(strong) = self.upgrade() { strong.as_ref().read(source, cache, context) } else { Err(Error::NoMatch) } } } /// Blanket implementation for `RcWeak where T: Parser` /// This allows weak references to reference-counted parsers impl Parser for RcWeak where T: Parser, { type Output = T::Output; fn id(&self) -> u64 { // For weak references, we need to upgrade to get the id // If the upgrade fails, we'll generate a default id based on the type if let Some(strong) = self.upgrade() { strong.as_ref().id() } else { // If the reference is dead, generate a consistent id based on the type let mut hasher = DefaultHasher::new(); TypeId::of::>().hash(&mut hasher); hasher.finish() } } fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, context: &mut Ctx, ) -> ParseResult where S: Parsable, { if let Some(strong) = self.upgrade() { strong.as_ref().read(source, cache, context) } else { Err(Error::NoMatch) } } } #[cfg(test)] mod tests { use super::*; use crate::cache::{BasicCache, NoCache}; use std::io::Cursor; #[test] fn test_source_creation() { let data = b"hello world"; let cursor = Cursor::new(data); let source = Source::new(cursor); assert_eq!(source.index, 0); assert!(source.stack.is_empty()); } #[test] fn test_source_peek1() { let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let byte = source.peek1().unwrap(); assert_eq!(byte, b'h'); // Position should not change after peek assert_eq!(source.index, 0); // Peek again should give same result let byte2 = source.peek1().unwrap(); assert_eq!(byte2, b'h'); } #[test] fn test_source_peek1_empty() { let data = b""; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let result = source.peek1(); assert!(matches!(result, Err(Error::NoMatch))); } #[test] fn test_source_peek_multiple() { let data = b"hello world"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let bytes = source.peek(5).unwrap(); assert_eq!(bytes, b"hello"); // Position should not change after peek assert_eq!(source.index, 0); // Peek more bytes let bytes = source.peek(11).unwrap(); assert_eq!(bytes, b"hello world"); } #[test] fn test_source_peek_beyond_end() { let data = b"hi"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let result = source.peek(5); assert!(matches!(result, Err(Error::NoMatch))); } #[test] fn test_source_advance() { let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); source.advance(2); assert_eq!(source.index, 2); let byte = source.peek1().unwrap(); assert_eq!(byte, b'l'); source.advance(3); assert_eq!(source.index, 5); let result = source.peek1(); assert!(matches!(result, Err(Error::NoMatch))); } #[test] fn test_source_read() { let data = b"hello world"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let bytes = source.read(5).unwrap(); assert_eq!(bytes, b"hello"); assert_eq!(source.index, 5); let byte = source.peek1().unwrap(); assert_eq!(byte, b' '); let bytes = source.read(6).unwrap(); assert_eq!(bytes, b" world"); assert_eq!(source.index, 11); } #[test] fn test_source_read_beyond_end() { let data = b"hi"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let result = source.read(5); assert!(matches!(result, Err(Error::NoMatch))); assert_eq!(source.index, 0); // Position should not change on error } #[test] fn test_source_push_pop() { let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); source.advance(2); assert_eq!(source.index, 2); source.push(); assert_eq!(source.stack.len(), 1); source.advance(2); assert_eq!(source.index, 4); source.pop(); assert_eq!(source.index, 2); assert_eq!(source.stack.len(), 0); } #[test] fn test_source_push_commit() { let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); source.advance(2); source.push(); source.advance(2); assert_eq!(source.index, 4); source.commit(); assert_eq!(source.index, 4); // Position stays the same assert_eq!(source.stack.len(), 0); } #[test] fn test_source_nested_push_pop() { let data = b"hello world"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); // First level source.advance(2); source.push(); // Second level source.advance(3); source.push(); // Third level source.advance(2); assert_eq!(source.index, 7); // Pop third level source.pop(); assert_eq!(source.index, 5); // Pop second level source.pop(); assert_eq!(source.index, 2); } #[test] fn test_source_empty_stack_pop() { let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); source.advance(3); source.pop(); // Should reset to 0 assert_eq!(source.index, 0); } #[test] fn test_unit_parser() { let data = b"anything"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let unit_parser = (); parse(unit_parser, &mut source).unwrap(); assert_eq!(source.index, 0); // Should not advance } #[test] fn test_unit_parser_empty_input() { let data = b""; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let unit_parser = (); parse(unit_parser, &mut source).unwrap(); } #[test] fn test_parse_global_function() { let data = b"test"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let unit_parser = (); parse(unit_parser, &mut source).unwrap(); } #[test] fn test_parse_with_cache_global_function() { let data = b"test"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let mut cache = NoCache; let unit_parser = (); parse_with_cache(unit_parser, &mut source, &mut cache).unwrap(); } #[test] fn test_source_position_management() { let data = b"0123456789"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); // Test sequential reads for i in 0..10 { let byte = source.peek1().unwrap(); assert_eq!(byte, b'0' + i as u8); source.advance(1); assert_eq!(source.index, i + 1); } // Should be at end let result = source.peek1(); assert!(matches!(result, Err(Error::NoMatch))); } #[test] fn test_source_large_advance() { let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); source.advance(1000); assert_eq!(source.index, 1000); // Should fail to read let result = source.peek1(); assert!(matches!(result, Err(Error::NoMatch))); } #[test] fn test_source_zero_read() { let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let bytes = source.read(0).unwrap(); assert_eq!(bytes, b""); assert_eq!(source.index, 0); } #[test] fn test_source_zero_peek() { let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let bytes = source.peek(0).unwrap(); assert_eq!(bytes, b""); assert_eq!(source.index, 0); } #[test] fn test_id_implementation_literal_parsers() { // Test that Literal correctly implements custom id() method // Different Literal parsers should have different IDs since they have different parameters use crate::literal::Literal; let literal1 = Literal::from_str("hello"); let literal2 = Literal::from_str("world"); // These should have different IDs because they have different content assert_ne!( >::id(&literal1), >::id(&literal2), "Different Literal instances should have different IDs to avoid cache conflicts" ); // Same content should have same ID let literal3 = Literal::from_str("hello"); assert_eq!( >::id(&literal1), >::id(&literal3), "Literal instances with same content should have same ID for cache efficiency" ); } #[test] fn test_id_implementation_unit_parser() { // Unit parser () uses default id() implementation // Since it has no parameters, using default implementation is correct let unit1 = (); let unit2 = (); // These should have the same ID since they're identical assert_eq!( <() as crate::parser::Parser<()>>::id(&unit1), <() as crate::parser::Parser<()>>::id(&unit2), "Unit parsers should have same ID since they're functionally identical" ); } #[test] fn test_parser_caching_behavior() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let mut cache = BasicCache::new(); let literal = Literal::from_str("hello"); // First parse should work and cache result let result1 = parse_with_cache(literal.clone(), &mut source, &mut cache).unwrap(); assert_eq!(result1, b"hello".as_slice().into()); // Reset position source.index = 0; // Second parse should use cached result let result2 = parse_with_cache(literal, &mut source, &mut cache).unwrap(); assert_eq!(result2, b"hello".as_slice().into()); } #[test] fn test_arc_parser() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let literal = Arc::new(Literal::from_str("hello")); let result = parse(literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_rc_parser() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let literal = Rc::new(Literal::from_str("hello")); let result = parse(literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_arc_mutex_parser() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let literal = Arc::new(Mutex::new(Literal::from_str("hello"))); let result = parse(literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_arc_rwlock_parser() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let literal = Arc::new(RwLock::new(Literal::from_str("hello"))); let result = parse(literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_rc_refcell_parser() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let literal = Rc::new(RefCell::new(Literal::from_str("hello"))); let result = parse(literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_std_mutex_parser() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let literal = StdMutex::new(Literal::from_str("hello")); let result = parse(literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_std_rwlock_parser() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let literal = StdRwLock::new(Literal::from_str("hello")); let result = parse(literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_arc_std_mutex_parser() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let literal = Arc::new(StdMutex::new(Literal::from_str("hello"))); let result = parse(literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_arc_std_rwlock_parser() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let literal = Arc::new(StdRwLock::new(Literal::from_str("hello"))); let result = parse(literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_smart_pointer_parser_id_consistency() { use crate::literal::Literal; let base_literal = Literal::from_str("hello"); let arc_mutex = Arc::new(Mutex::new(Literal::from_str("hello"))); let arc_rwlock = Arc::new(RwLock::new(Literal::from_str("hello"))); let rc_refcell = Rc::new(RefCell::new(Literal::from_str("hello"))); // All should have the same ID since they wrap the same parser assert_eq!( >::id(&base_literal), > as crate::parser::Parser<()>>::id(&arc_mutex) ); assert_eq!( >::id(&base_literal), > as crate::parser::Parser<()>>::id(&arc_rwlock) ); assert_eq!( >::id(&base_literal), > as crate::parser::Parser<()>>::id(&rc_refcell) ); } #[test] fn test_arc_weak_parser_alive() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let arc_literal = Arc::new(Literal::from_str("hello")); let weak_literal = Arc::downgrade(&arc_literal); // ID should match the strong reference assert_eq!( as crate::parser::Parser<()>>::id(&arc_literal), as crate::parser::Parser<()>>::id(&weak_literal) ); // Should work while the Arc is alive let result = parse(weak_literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_arc_weak_parser_dropped() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let weak_literal = { let arc_literal = Arc::new(Literal::from_str("hello")); Arc::downgrade(&arc_literal) }; // arc_literal is dropped here // Should fail with NoMatch since the Arc was dropped let result = parse(weak_literal, &mut source); assert!(matches!(result, Err(Error::NoMatch))); } #[test] fn test_rc_weak_parser_alive() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let rc_literal = Rc::new(Literal::from_str("hello")); let weak_literal = Rc::downgrade(&rc_literal); // ID should match the strong reference assert_eq!( as crate::parser::Parser<()>>::id(&rc_literal), as crate::parser::Parser<()>>::id(&weak_literal) ); // Should work while the Rc is alive let result = parse(weak_literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_rc_weak_parser_dropped() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let weak_literal = { let rc_literal = Rc::new(Literal::from_str("hello")); Rc::downgrade(&rc_literal) }; // rc_literal is dropped here // Should fail with NoMatch since the Rc was dropped let result = parse(weak_literal, &mut source); assert!(matches!(result, Err(Error::NoMatch))); } #[test] fn test_arc_weak_mutex_parser_alive() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let arc_literal = Arc::new(Mutex::new(Literal::from_str("hello"))); let weak_literal = Arc::downgrade(&arc_literal); // ID should match the strong reference assert_eq!( > as crate::parser::Parser<()>>::id(&arc_literal), > as crate::parser::Parser<()>>::id(&weak_literal) ); // Should work while the Arc is alive let result = parse(weak_literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_arc_weak_mutex_parser_dropped() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let weak_literal = { let arc_literal = Arc::new(Mutex::new(Literal::from_str("hello"))); Arc::downgrade(&arc_literal) }; // arc_literal is dropped here // Should fail with NoMatch since the Arc was dropped let result = parse(weak_literal, &mut source); assert!(matches!(result, Err(Error::NoMatch))); } #[test] fn test_arc_weak_rwlock_parser_alive() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let arc_literal = Arc::new(RwLock::new(Literal::from_str("hello"))); let weak_literal = Arc::downgrade(&arc_literal); // ID should match the strong reference assert_eq!( > as crate::parser::Parser<()>>::id(&arc_literal), > as crate::parser::Parser<()>>::id(&weak_literal) ); // Should work while the Arc is alive let result = parse(weak_literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_rc_weak_refcell_parser_alive() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let rc_literal = Rc::new(RefCell::new(Literal::from_str("hello"))); let weak_literal = Rc::downgrade(&rc_literal); // ID should match the strong reference assert_eq!( > as crate::parser::Parser<()>>::id(&rc_literal), > as crate::parser::Parser<()>>::id(&weak_literal) ); // Should work while the Rc is alive let result = parse(weak_literal, &mut source).unwrap(); assert_eq!(result, b"hello".as_slice().into()); } #[test] fn test_rc_weak_refcell_parser_dropped() { use crate::literal::Literal; let data = b"hello"; let cursor = Cursor::new(data); let mut source = Source::new(cursor); let weak_literal = { let rc_literal = Rc::new(RefCell::new(Literal::from_str("hello"))); Rc::downgrade(&rc_literal) }; // rc_literal is dropped here // Should fail with NoMatch since the Rc was dropped let result = parse(weak_literal, &mut source); assert!(matches!(result, Err(Error::NoMatch))); } }