use crate::formatter::{block::BlockMode, condition::ConditionalToken};
pub(super) enum Lex<'a> {
Text(&'a str),
BlockStart,
BlockEnd,
Variable,
Conditional,
Prefix,
Suffix,
Fallback,
Or,
And,
Not,
Space,
Escape,
}
impl<'a> Lex<'a> {
pub fn to_str(&self) -> &'a str {
match self {
Lex::Text(str) => str,
Lex::BlockStart => "{",
Lex::BlockEnd => "}",
Lex::Variable => "$",
Lex::Conditional => "@",
Lex::Prefix => "<",
Lex::Suffix => ">",
Lex::Fallback => "?",
Lex::Or => "|",
Lex::And => "&",
Lex::Not => "!",
Lex::Space => " ",
Lex::Escape => "\\",
}
}
pub(super) fn to_block_mode(&self) -> Option<BlockMode> {
match self {
Lex::Conditional => Some(BlockMode::Condition),
Lex::Prefix => Some(BlockMode::Prefix),
Lex::Suffix => Some(BlockMode::Suffix),
Lex::Fallback => Some(BlockMode::Fallback),
_ => None,
}
}
pub(super) fn to_condition_token(&self) -> Option<ConditionalToken<'a>> {
match self {
Lex::Or => Some(ConditionalToken::Or),
Lex::And => Some(ConditionalToken::And),
Lex::Not => Some(ConditionalToken::Not),
_ => None,
}
}
}
pub(super) fn lex_str<'a>(str: &'a str) -> Vec<Lex<'a>> {
let mut lex = Vec::new();
let mut text_start: Option<usize> = None;
for (i, byte) in str.bytes().enumerate() {
let new_lex = match byte {
b'{' => Some(Lex::BlockStart),
b'}' => Some(Lex::BlockEnd),
b'$' => Some(Lex::Variable),
b'@' => Some(Lex::Conditional),
b'<' => Some(Lex::Prefix),
b'>' => Some(Lex::Suffix),
b'?' => Some(Lex::Fallback),
b'|' => Some(Lex::Or),
b'&' => Some(Lex::And),
b'!' => Some(Lex::Not),
b' ' => Some(Lex::Space),
b'\\' => Some(Lex::Escape),
_ => None,
};
if let Some(token) = new_lex {
if let Some(start) = text_start.take() {
lex.push(Lex::Text(&str[start..i]));
}
lex.push(token);
} else {
text_start.get_or_insert(i);
}
}
if let Some(start) = text_start.take() {
lex.push(Lex::Text(&str[start..]));
}
lex
}