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;
#[ derive( Debug, Clone ) ]
pub enum ParseError
{
InvalidToken
{
token: String,
position: usize,
expected: String,
},
ValidationFailed
{
token: String,
position: usize,
reason: String,
},
UnexpectedEof
{
position: usize,
expected: String,
},
InvalidKeyValuePair( String ),
UnknownKey( 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
{
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
}
}
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 >,
{
pub fn new( input: &'a str, delimiters: Vec< &'a str >, parser: F ) -> Self
{
Self
{
input,
delimiters,
parser_func: parser,
position: 0,
_phantom: PhantomData,
}
}
fn find_next_token( &mut self ) -> Option< &'a str >
{
loop
{
if self.position >= self.input.len()
{
return None;
}
let remaining = &self.input[ self.position.. ];
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();
},
_ => {} }
}
}
let token = if let Some( delim_pos ) = earliest_delim_pos
{
let token = &remaining[ ..delim_pos ];
self.position += delim_pos + earliest_delim_len;
token
}
else
{
let token = remaining;
self.position = self.input.len();
token
};
if !token.is_empty()
{
return Some( 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 ) )
}
}
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 )
}
#[ derive( Debug, Clone, PartialEq, Eq ) ]
pub enum ParsedToken< 'a >
{
Command( &'a str ),
KeyValue
{
key: &'a str,
value: &'a str,
},
Flag( &'a str ),
Positional( &'a str ),
}
impl< 'a > ParsedToken< 'a >
{
pub fn as_str( &self ) -> &'a str
{
match self
{
ParsedToken ::Command( s ) => s,
ParsedToken ::KeyValue { key, .. } => key, ParsedToken ::Flag( s ) => s,
ParsedToken ::Positional( s ) => s,
}
}
pub fn is_command( &self ) -> bool
{
matches!( self, ParsedToken ::Command( _ ) )
}
pub fn is_flag( &self ) -> bool
{
matches!( self, ParsedToken ::Flag( _ ) )
}
pub fn is_key_value( &self ) -> bool
{
matches!( self, ParsedToken ::KeyValue { .. } )
}
pub fn is_positional( &self ) -> bool
{
matches!( self, ParsedToken ::Positional( _ ) )
}
}
pub trait ParserIntegrationExt
{
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;
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;
fn parse_command_line< 'a >( &'a self ) -> impl Iterator< Item = Result< ParsedToken< 'a >, ParseError > > + 'a;
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,
{
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 )
}
}