strs_tools 0.49.1

Tools to manipulate strings.
Documentation
//! Parser integration for single-pass string processing operations.
//!
//! This module provides integrated parsing operations that combine tokenization,
//! validation, and transformation in single passes for optimal performance.

mod command_parser;
mod manual_split;

pub use command_parser::CommandParser;
pub use manual_split::ManualSplitIterator;

use std ::marker ::PhantomData;
use crate ::string ::zero_copy ::ZeroCopyStringExt;

/// Error types for parsing operations
#[ derive( Debug, Clone ) ]
pub enum ParseError
{
  /// Invalid token encountered during parsing
  InvalidToken
  {
  /// The token that failed to parse
  token: String,
  /// Position in the input where the token was found
  position: usize,
  /// Description of what was expected
  expected: String,
 },
  /// Validation failed for a token
  ValidationFailed
  {
  /// The token that failed validation
  token: String,
  /// Position in the input where the token was found
  position: usize,
  /// Reason why validation failed
  reason: String,
 },
  /// Unexpected end of input
  UnexpectedEof
  {
  /// Position where end of input was encountered
  position: usize,
  /// Description of what was expected
  expected: String,
 },
  /// Invalid key-value pair format
  InvalidKeyValuePair( String ),
  /// Unknown key in parsing context
  UnknownKey( String ),
  /// I/O error during streaming operations (not cloneable, stored as string)
  IoError( String ),
}

impl std ::fmt ::Display for ParseError
{
  fn fmt( &self, f: &mut std ::fmt ::Formatter< '_ > ) -> std ::fmt ::Result
  {
  match self
  {
   ParseError ::InvalidToken { token, position, expected } =>
  write!( f, "Invalid token '{}' at position {}, expected: {}", token, position, expected ),
   ParseError ::ValidationFailed { token, position, reason } =>
  write!( f, "Validation failed for '{}' at position {} : {}", token, position, reason ),
   ParseError ::UnexpectedEof { position, expected } =>
  write!( f, "Unexpected end of input at position {}, expected: {}", position, expected ),
   ParseError ::InvalidKeyValuePair( pair ) =>
  write!( f, "Invalid key-value pair format: '{}'", pair ),
   ParseError ::UnknownKey( key ) =>
  write!( f, "Unknown key: '{}'", key ),
   ParseError ::IoError( e ) =>
  write!( f, "I/O error: {}", e ),
 }
 }
}

impl std ::error ::Error for ParseError {}

impl ParseError
{
  /// Add position information to error
  pub fn with_position( mut self, pos: usize ) -> Self
  {
  match &mut self
  {
   ParseError ::InvalidToken { position, .. } => *position = pos,
   ParseError ::ValidationFailed { position, .. } => *position = pos,
   ParseError ::UnexpectedEof { position, .. } => *position = pos,
   _ => {},
 }
  self
 }
}

/// Single-pass token parsing iterator that combines splitting and parsing
pub struct TokenParsingIterator< 'a, F, T >
{
  input: &'a str,
  delimiters: Vec< &'a str >,
  parser_func: F,
  position: usize,
  _phantom: PhantomData< T >,
}

impl< 'a, F, T > std ::fmt ::Debug for TokenParsingIterator< 'a, F, T >
{
  fn fmt( &self, f: &mut std ::fmt ::Formatter< '_ > ) -> std ::fmt ::Result
  {
  f.debug_struct( "TokenParsingIterator" )
   .field( "input", &self.input )
   .field( "delimiters", &self.delimiters )
   .field( "position", &self.position )
   .field( "parser_func", &"< function >" )
   .finish()
 }
}

impl< 'a, F, T > TokenParsingIterator< 'a, F, T >
where
  F: Fn( &str ) -> Result< T, ParseError >,
{
  /// Create new token parsing iterator
  pub fn new( input: &'a str, delimiters: Vec< &'a str >, parser: F ) -> Self
  {
  Self
  {
   input,
   delimiters,
   parser_func: parser,
   position: 0,
   _phantom: PhantomData,
 }
 }

  /// Find next token using simple string operations
  fn find_next_token( &mut self ) -> Option< &'a str >
  {
  loop
  {
   if self.position >= self.input.len()
   {
  return None;
 }

   let remaining = &self.input[ self.position.. ];
   
   // Find the earliest delimiter match
   let mut earliest_delim_pos = None;
   let mut earliest_delim_len = 0;
   
   for delim in &self.delimiters
   {
  if let Some( pos ) = remaining.find( delim )
  {
   match earliest_delim_pos
   {
  None => 
  {
   earliest_delim_pos = Some( pos );
   earliest_delim_len = delim.len();
 },
  Some( current_pos ) if pos < current_pos =>
  {
   earliest_delim_pos = Some( pos );
   earliest_delim_len = delim.len();
 },
  _ => {} // Keep current earliest
 }
 }
 }
   
   let token = if let Some( delim_pos ) = earliest_delim_pos
   {
  // Token is everything before the delimiter
  let token = &remaining[ ..delim_pos ];
  self.position += delim_pos + earliest_delim_len;
  token
 }
   else
   {
  // No delimiter found, rest of input is the token
  let token = remaining;
  self.position = self.input.len();
  token
 };
   
   if !token.is_empty()
   {
  return Some( token );
 }
   
   // If token is empty, continue loop to find next non-empty token
 }
 }
}

impl< 'a, F, T > Iterator for TokenParsingIterator< 'a, F, T >
where
  F: Fn( &str ) -> Result< T, ParseError >,
{
  type Item = Result< T, ParseError >;

  fn next( &mut self ) -> Option< Self ::Item >
  {
  let token = self.find_next_token()?;
  Some( ( self.parser_func )( token ) )
 }
}

/// Parse and split in single operation
pub fn parse_and_split< 'a, T, F >(
  input: &'a str,
  delimiters: &'a [ &'a str ],
  parser: F,
) -> TokenParsingIterator< 'a, F, T >
where
  F: Fn( &str ) -> Result< T, ParseError >,
{
  TokenParsingIterator ::new( input, delimiters.to_vec(), parser )
}

/// Parsed token types for structured command-line parsing
#[ derive( Debug, Clone, PartialEq, Eq ) ]
pub enum ParsedToken< 'a >
{
  /// Command name
  Command( &'a str ),
  /// Key-value pair argument
  KeyValue
  {
  /// The key part of the pair
  key: &'a str,
  /// The value part of the pair
  value: &'a str,
 },
  /// Flag argument (starts with --)
  Flag( &'a str ),
  /// Positional argument
  Positional( &'a str ),
}

impl< 'a > ParsedToken< 'a >
{
  /// Get the string content of the token
  pub fn as_str( &self ) -> &'a str
  {
  match self
  {
   ParsedToken ::Command( s ) => s,
   ParsedToken ::KeyValue { key, .. } => key, // Return key by default
   ParsedToken ::Flag( s ) => s,
   ParsedToken ::Positional( s ) => s,
 }
 }

  /// Check if this token is a specific type
  pub fn is_command( &self ) -> bool
  {
  matches!( self, ParsedToken ::Command( _ ) )
 }

  /// Check if this token is a flag
  pub fn is_flag( &self ) -> bool
  {
  matches!( self, ParsedToken ::Flag( _ ) )
 }

  /// Check if this token is a key-value pair
  pub fn is_key_value( &self ) -> bool
  {
  matches!( self, ParsedToken ::KeyValue { .. } )
 }

  /// Check if this token is a positional argument
  pub fn is_positional( &self ) -> bool
  {
  matches!( self, ParsedToken ::Positional( _ ) )
 }
}

/// Extension trait adding parser integration to string types
pub trait ParserIntegrationExt
{
  /// Parse tokens while splitting in single pass
  fn split_and_parse< 'a, T: 'a, F >(
  &'a self,
  delimiters: &'a [ &'a str ],
  parser: F,
 ) -> impl Iterator< Item = Result< T, ParseError > > + 'a
  where
  F: Fn( &str ) -> Result< T, ParseError > + 'a;

  /// Split with validation using zero-copy operations
  fn split_with_validation< 'a, F >(
  &'a self,
  delimiters: &'a [ &'a str ],
  validator: F,
 ) -> impl Iterator< Item = Result< &'a str, ParseError > > + 'a
  where
  F: Fn( &str ) -> bool + 'a;

  /// Parse structured command line arguments
  fn parse_command_line< 'a >( &'a self ) -> impl Iterator< Item = Result< ParsedToken< 'a >, ParseError > > + 'a;

  /// Count tokens that pass validation without allocation
  fn count_valid_tokens< F >( &self, delimiters: &[ &str ], validator: F ) -> usize
  where
  F: Fn( &str ) -> bool;
}

impl ParserIntegrationExt for str
{
  fn split_and_parse< 'a, T: 'a, F >(
  &'a self,
  delimiters: &'a [ &'a str ],
  parser: F,
 ) -> impl Iterator< Item = Result< T, ParseError > > + 'a
  where
  F: Fn( &str ) -> Result< T, ParseError > + 'a,
  {
  parse_and_split( self, delimiters, parser )
 }

  fn split_with_validation< 'a, F >(
  &'a self,
  delimiters: &'a [ &'a str ],
  validator: F,
 ) -> impl Iterator< Item = Result< &'a str, ParseError > > + 'a
  where
  F: Fn( &str ) -> bool + 'a,
  {
  // Use manual splitting that can return references to original string
  ManualSplitIterator ::new( self, delimiters, validator )
 }

  fn parse_command_line< 'a >( &'a self ) -> impl Iterator< Item = Result< ParsedToken< 'a >, ParseError > > + 'a
  {
  CommandParser ::new( self ).parse_structured()
 }

  fn count_valid_tokens< F >( &self, delimiters: &[ &str ], validator: F ) -> usize
  where
  F: Fn( &str ) -> bool,
  {
  self.zero_copy_split( delimiters )
   .filter( |segment| validator( segment.as_str() ) )
   .count()
 }
}

impl ParserIntegrationExt for String
{
  fn split_and_parse< 'a, T: 'a, F >(
  &'a self,
  delimiters: &'a [ &'a str ],
  parser: F,
 ) -> impl Iterator< Item = Result< T, ParseError > > + 'a
  where
  F: Fn( &str ) -> Result< T, ParseError > + 'a,
  {
  self.as_str().split_and_parse( delimiters, parser )
 }

  fn split_with_validation< 'a, F >(
  &'a self,
  delimiters: &'a [ &'a str ],
  validator: F,
 ) -> impl Iterator< Item = Result< &'a str, ParseError > > + 'a
  where
  F: Fn( &str ) -> bool + 'a,
  {
  self.as_str().split_with_validation( delimiters, validator )
 }

  fn parse_command_line< 'a >( &'a self ) -> impl Iterator< Item = Result< ParsedToken< 'a >, ParseError > > + 'a
  {
  self.as_str().parse_command_line()
 }

  fn count_valid_tokens< F >( &self, delimiters: &[ &str ], validator: F ) -> usize
  where
  F: Fn( &str ) -> bool,
  {
  self.as_str().count_valid_tokens( delimiters, validator )
 }
}