use crate::error::Result;
use std::{
iter::{Enumerate, Peekable},
str::Chars,
};
pub type ParserState<'a> = Peekable<Enumerate<Chars<'a>>>;
#[macro_export]
macro_rules! create_state {
($json_str:ident) => {
$json_str.as_ref().chars().enumerate().peekable()
};
($json_str:expr) => {
$json_str.chars().enumerate().peekable()
};
}
pub fn peek(state: &mut ParserState) -> Option<char> {
state.peek().map(|(_, c)| *c)
}
pub fn advance(state: &mut ParserState) -> Option<char> {
state.next().map(|(_, c)| c)
}
pub fn assert_char(state: &mut ParserState, mut c: char, ignore_case: bool) -> Result<()> {
if ignore_case {
c = c.to_ascii_lowercase();
}
if let Some(mut advance_value) = advance(state) {
if ignore_case {
advance_value = advance_value.to_ascii_lowercase();
advance_value = advance_value.to_ascii_lowercase();
}
if advance_value != c {
Err(format!("`{advance_value}` is not equal to `{c}`").into())
} else {
Ok(())
}
} else {
Err("No char returned".into())
}
}
pub fn check_char(state: &mut ParserState, check_against_char: char) -> bool {
match peek(state) {
Some(c) if c == check_against_char => {
advance(state);
true
}
_ => false,
}
}
pub fn assert_string<S: AsRef<str>>(
state: &mut ParserState,
string: S,
ignore_case: bool,
) -> Result<()> {
for c in string.as_ref().chars() {
if let Err(e) = assert_char(state, c, ignore_case) {
return Err(format!("failed assert expected \"{}\"\n{e}", string.as_ref()).into());
}
}
Ok(())
}
pub fn consume_number(state: &mut ParserState) -> String {
state
.take(
state
.clone()
.take_while(|(_, c)| is_number_part(*c))
.count(),
)
.map(|(_, c)| c)
.collect::<String>()
}
fn is_number_part(character: char) -> bool {
match character {
'-' | '.' => true,
c => c >= (48 as char) && c <= (57 as char),
}
}
pub fn consume_whitespace(state: &mut ParserState) {
while is_whitespace(state) {
advance(state);
}
}
pub fn is_whitespace(state: &mut ParserState) -> bool {
matches!(peek(state), Some(' ' | '\t' | '\n'))
}