use super::{ ParseError, ParsedToken };
#[ derive( Debug, Clone, Copy ) ]
enum ParsingContext
{
Command,
Arguments,
#[ allow( dead_code ) ]
Value,
}
#[ derive( Debug, Clone ) ]
pub struct CommandParser< 'a >
{
input: &'a str,
token_delimiters: Vec< &'a str >,
kv_separator: &'a str,
flag_prefix: &'a str,
}
impl< 'a > CommandParser< 'a >
{
pub fn new( input: &'a str ) -> Self
{
Self
{
input,
token_delimiters: vec![ " ", "\t" ],
kv_separator: ":",
flag_prefix: "--",
}
}
pub fn with_token_delimiters( mut self, delimiters: Vec< &'a str > ) -> Self
{
self.token_delimiters = delimiters;
self
}
pub fn with_kv_separator( mut self, separator: &'a str ) -> Self
{
self.kv_separator = separator;
self
}
pub fn with_flag_prefix( mut self, prefix: &'a str ) -> Self
{
self.flag_prefix = prefix;
self
}
pub fn parse_structured( self ) -> impl Iterator< Item = Result< ParsedToken< 'a >, ParseError > > + 'a
{
StructuredParsingIterator
{
parser: self,
position: 0,
current_context: ParsingContext ::Command,
pending_key: None,
}
}
}
struct StructuredParsingIterator< 'a >
{
parser: CommandParser< 'a >,
position: usize,
current_context: ParsingContext,
pending_key: Option< &'a str >,
}
impl< 'a > StructuredParsingIterator< 'a >
{
fn find_next_token( &mut self ) -> Option< &'a str >
{
loop
{
if self.position >= self.parser.input.len()
{
return None;
}
let remaining = &self.parser.input[ self.position.. ];
let mut earliest_delim_pos = None;
let mut earliest_delim_len = 0;
for delim in &self.parser.token_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_start, token_end) = if let Some( delim_pos ) = earliest_delim_pos
{
let token_start = self.position;
let token_end = self.position + delim_pos;
self.position += delim_pos + earliest_delim_len;
(token_start, token_end)
}
else
{
let token_start = self.position;
let token_end = self.parser.input.len();
self.position = self.parser.input.len();
(token_start, token_end)
};
if token_start < token_end
{
let token = &self.parser.input[ token_start..token_end ];
if !token.is_empty()
{
return Some( token );
}
}
}
}
fn parse_argument_token( &mut self, token: &'a str ) -> Result< ParsedToken< 'a >, ParseError >
{
if token.contains( self.parser.kv_separator )
{
let separator_pos = token.find( self.parser.kv_separator ).unwrap();
let key_part = &token[ ..separator_pos ];
let value = &token[ separator_pos + self.parser.kv_separator.len().. ];
let key = if key_part.starts_with( self.parser.flag_prefix )
{
&key_part[ self.parser.flag_prefix.len().. ]
}
else
{
key_part
};
if key.is_empty()
{
Err( ParseError ::InvalidKeyValuePair( token.to_string() ) )
}
else if value.is_empty()
{
self.current_context = ParsingContext ::Value;
self.pending_key = Some( key );
Ok( ParsedToken ::Positional( token ) ) }
else
{
Ok( ParsedToken ::KeyValue { key, value } )
}
}
else if token.starts_with( self.parser.flag_prefix )
{
let flag_name = &token[ self.parser.flag_prefix.len().. ];
Ok( ParsedToken ::Flag( flag_name ) )
}
else
{
Ok( ParsedToken ::Positional( token ) )
}
}
}
impl< 'a > Iterator for StructuredParsingIterator< 'a >
{
type Item = Result< ParsedToken< 'a >, ParseError >;
fn next( &mut self ) -> Option< Self ::Item >
{
loop
{
let token = self.find_next_token()?;
match self.current_context
{
ParsingContext ::Command =>
{
self.current_context = ParsingContext ::Arguments;
return Some( Ok( ParsedToken ::Command( token ) ) );
},
ParsingContext ::Arguments =>
{
match self.parse_argument_token( token )
{
Ok( ParsedToken ::Positional( _ ) ) if self.pending_key.is_some() =>
{
},
other => return Some( other ),
}
},
ParsingContext ::Value =>
{
self.current_context = ParsingContext ::Arguments;
if let Some( key ) = self.pending_key.take()
{
return Some( Ok( ParsedToken ::KeyValue { key, value: token } ) );
}
return Some( Ok( ParsedToken ::Positional( token ) ) );
},
}
}
}
}