use crate::consts::{ESCAPE_CHAR, LIT_CHAR};
use std::{iter::Peekable, str::Chars};
pub struct ArgParser {
values: Vec<String>,
previous: Option<char>,
lit_count: usize,
no_previous: bool,
}
#[derive(Debug)]
pub enum GreedyState {
Deterred(usize),
Greedy,
Never,
}
impl ArgParser {
pub(crate) fn new() -> Self {
Self {
values: vec![],
previous: None,
lit_count: 0,
no_previous: false,
}
}
pub(crate) fn strip_literal(&mut self, args: &str) -> String {
self.args_to_vec(args, ',', GreedyState::Greedy)[0].to_owned()
}
pub(crate) fn args_with_len(&mut self, args: &str, length: usize) -> Option<Vec<String>> {
let greedy_state = if length > 1 {
GreedyState::Deterred(length - 1)
} else {
GreedyState::Greedy
};
let args: Vec<_> = self.args_to_vec(args, ',', greedy_state);
if args.len() < length {
return None;
}
Some(args)
}
pub(crate) fn args_to_vec(
&mut self,
arg_values: &str,
delimiter: char,
mut greedy_state: GreedyState,
) -> Vec<String> {
let mut value = String::new();
let mut arg_iter = arg_values.chars().peekable();
while let Some(ch) = arg_iter.next() {
if ch == delimiter {
self.branch_delimiter(ch, &mut value, &mut greedy_state);
} else if ch == ESCAPE_CHAR {
self.branch_escape_char(ch, &mut value, arg_iter.peek());
} else {
if ch == LIT_CHAR {
self.branch_literal_char(ch, &mut value, &mut arg_iter);
} else {
value.push(ch);
}
}
if self.no_previous {
self.previous.replace('0');
self.no_previous = false;
} else {
self.previous.replace(ch);
}
} self.values.push(value);
std::mem::take(&mut self.values)
}
fn branch_delimiter(&mut self, ch: char, value: &mut String, greedy_state: &mut GreedyState) {
if self.lit_count > 0 || self.previous.unwrap_or('0') == ESCAPE_CHAR {
value.push(ch);
} else {
match greedy_state {
GreedyState::Deterred(count) => {
self.values.push(std::mem::take(value));
let count = *count - 1;
if count > 0 {
*greedy_state = GreedyState::Deterred(count);
} else {
*greedy_state = GreedyState::Greedy;
}
self.no_previous = true;
}
GreedyState::Greedy => {
value.push(ch);
}
GreedyState::Never => {
self.values.push(std::mem::take(value));
}
} } }
fn branch_escape_char(&mut self, ch: char, value: &mut String, next: Option<&char>) {
if self.previous.unwrap_or(' ') == ESCAPE_CHAR {
self.no_previous = true;
} else if let Some(&LIT_CHAR) = next {
} else {
value.push(ch);
}
}
fn branch_literal_char(
&mut self,
ch: char,
value: &mut String,
arg_iter: &mut Peekable<Chars>,
) {
if self.previous.unwrap_or('0') == ESCAPE_CHAR {
self.lit_count += 1;
if self.lit_count > 1 {
value.push(ch);
}
else {
self.no_previous = true;
}
} else if let Some(&ch_next) = arg_iter.peek() {
if ch_next == ESCAPE_CHAR && self.lit_count >= 1 {
self.lit_count -= 1;
arg_iter.next(); if self.lit_count == 0 {
self.no_previous = true;
}
else {
value.push(LIT_CHAR);
value.push(ESCAPE_CHAR);
self.no_previous = true;
}
}
else {
value.push(ch);
}
}
else {
value.push(ch);
}
}
}