pub const DEFAULT_QUOTE_CHAR: char = '\\';
#[derive(PartialEq)]
pub enum CharacterClass {
Ignore,
KeepUnaltered,
KeepUnalteredButQuoteMeta,
}
pub struct RegexQuoteFixer {
pub lambda: Box<dyn Fn(char) -> bool>,
pub quote_char: char,
pub cc: CharacterClass,
}
impl RegexQuoteFixer {
pub fn from_chars(v: Vec<char>) -> Self {
Self {
lambda: Box::new(move |x| v.contains(&x)),
quote_char: DEFAULT_QUOTE_CHAR,
cc: CharacterClass::KeepUnalteredButQuoteMeta,
}
}
pub fn from_string(s: String) -> Self {
Self {
lambda: Box::new(move |x| s.contains(x)),
quote_char: DEFAULT_QUOTE_CHAR,
cc: CharacterClass::KeepUnalteredButQuoteMeta,
}
}
pub fn for_grep() -> Self {
Self::from_string("()?+{}".into())
}
pub fn from_lambda(lambda: Box<dyn Fn(char) -> bool>) -> Self {
Self {
lambda,
quote_char: DEFAULT_QUOTE_CHAR,
cc: CharacterClass::KeepUnalteredButQuoteMeta,
}
}
pub fn fix(&self, s: &str) -> String {
let mut ret = String::new();
struct CharacterClassState {
nth_char: usize,
depth: u8,
}
let mut inside_character_class = Option::<CharacterClassState>::None;
let mut quote_char = false;
for char in s.chars() {
if self.cc != CharacterClass::Ignore {
if let Some(cc) = &mut inside_character_class {
cc.nth_char += 1;
match char {
']' if cc.nth_char != 1 => cc.depth -= 1,
'[' => cc.depth += 1,
_ => {}
}
if cc.depth == 0 {
inside_character_class = None;
}
match self.cc {
CharacterClass::KeepUnalteredButQuoteMeta => match (quote_char, self.quote_char == char) {
(false, true) => quote_char = true,
(true, true) => {
quote_char = false;
ret.push(self.quote_char);
}
(true, false) => {
quote_char = false;
ret.push(self.quote_char);
ret.push(self.quote_char);
ret.push(char);
}
(false, false) => ret.push(char),
},
CharacterClass::KeepUnaltered => {
ret.push(char);
}
_ => {}
}
continue;
}
if char == '[' {
if quote_char {
ret.push(self.quote_char);
ret.push(char);
quote_char = false;
continue;
} else {
inside_character_class = Some(CharacterClassState {
nth_char: 0,
depth: 1,
});
ret.push(char);
continue;
}
}
}
if char == self.quote_char {
if quote_char {
ret.push(self.quote_char);
ret.push(self.quote_char);
quote_char = false;
continue;
} else {
quote_char = true;
continue;
}
}
if (self.lambda)(char) {
if quote_char {
ret.push(char);
quote_char = false;
continue;
} else {
ret.push(self.quote_char);
ret.push(char);
continue;
}
} else {
if quote_char {
ret.push(self.quote_char);
ret.push(char);
quote_char = false;
continue;
} else {
ret.push(char);
}
}
}
if quote_char {
ret.push(self.quote_char);
}
ret
}
}