use crate::parser::QueryParser;
#[derive(Debug, Default)]
pub struct WordComparer {
pub length: usize,
pub word: Vec<char>,
whitespace_postfix: bool,
break_line_postfix: bool,
full_block_delimiter_postfix: bool,
eof: bool,
delimiter: Option<char>,
optional_postfix: Vec<char>,
}
impl WordComparer {
pub fn new(word: &str) -> Self {
Self {
length: word.len(),
word: word.to_uppercase().chars().collect(),
whitespace_postfix: false,
break_line_postfix: false,
full_block_delimiter_postfix: false,
eof: false,
delimiter: None,
optional_postfix: vec![],
}
}
pub fn reach_eof(&self, parser: &QueryParser) -> bool {
parser.position + self.length >= parser.length
}
pub fn is_block_delimiter(ch: char) -> bool {
ch.is_ascii_whitespace()
}
pub fn is_any_delimiter(ch: char) -> bool {
ch == ',' || ch == '(' || ch == ')' || ch == '.' || Self::is_block_delimiter(ch)
}
pub fn is_break_line(ch: char) -> bool {
ch == '\r' || ch == '\n'
}
pub fn is_current_block_delimiter(parser: &QueryParser) -> bool {
Self::is_block_delimiter(parser.current())
}
pub fn is_current_break_line(parser: &QueryParser) -> bool {
Self::is_break_line(parser.current())
}
pub fn compare(&self, parser: &QueryParser) -> bool {
let mut position = 0;
while position < self.length {
if (parser.position + position) >= parser.length
|| self.word[position]
!= parser.text_v[parser.position + position].to_ascii_uppercase()
{
return false;
}
position += 1;
}
if self.reach_eof(parser) {
return self.eof;
}
if self.delimiter.is_none()
&& !self.full_block_delimiter_postfix
&& !self.whitespace_postfix
&& !self.break_line_postfix
&& self.optional_postfix.is_empty()
{
return true;
}
let next = parser.text_v[parser.position + position];
if let Some(delimiter) = self.delimiter
&& next == delimiter
{
return true;
}
if self.full_block_delimiter_postfix && Self::is_any_delimiter(next) {
return true;
}
if self.whitespace_postfix && Self::is_block_delimiter(next) {
return true;
}
if self.break_line_postfix && Self::is_break_line(next) {
return true;
}
for value in self.optional_postfix.iter() {
if *value == next {
return true;
}
}
false
}
pub fn with_eof(mut self) -> Self {
self.eof = true;
self
}
pub fn with_whitespace_postfix(mut self) -> Self {
self.whitespace_postfix = true;
self
}
pub fn with_break_line_postfix(mut self) -> Self {
self.break_line_postfix = true;
self
}
pub fn with_any_delimiter_postfix(mut self) -> Self {
self.full_block_delimiter_postfix = true;
self
}
pub fn with_delimiter(mut self, delimiter: char) -> Self {
self.delimiter = Some(delimiter);
self
}
pub fn with_optional_postfix(mut self, value: char) -> Self {
self.optional_postfix.push(value);
self
}
pub fn compare_with_block_delimiter(&self, parser: &QueryParser) -> bool {
self.compare(parser)
&& (self.reach_eof(parser) || Self::is_any_delimiter(parser.peek(self.length)))
}
}
#[cfg(test)]
mod tests {
use super::WordComparer;
use crate::parser::QueryParser;
#[test]
fn compare_is_case_insensitive_without_postfix_rules() {
let parser = QueryParser::new("select ");
assert!(WordComparer::new("SELECT").compare(&parser));
assert!(!WordComparer::new("FROM").compare(&parser));
}
#[test]
fn compare_honors_eof_and_postfix_rules() {
assert!(
WordComparer::new("x")
.with_eof()
.compare(&QueryParser::new("x"))
);
assert!(!WordComparer::new("x").compare(&QueryParser::new("x")));
assert!(
WordComparer::new("x")
.with_whitespace_postfix()
.compare(&QueryParser::new("x "))
);
assert!(
WordComparer::new("x")
.with_break_line_postfix()
.compare(&QueryParser::new("x\n"))
);
assert!(
WordComparer::new("x")
.with_any_delimiter_postfix()
.compare(&QueryParser::new("x,"))
);
assert!(
WordComparer::new("x")
.with_delimiter('(')
.compare(&QueryParser::new("x("))
);
assert!(
WordComparer::new("x")
.with_optional_postfix(',')
.compare(&QueryParser::new("x,"))
);
assert!(
!WordComparer::new("x")
.with_whitespace_postfix()
.compare(&QueryParser::new("x,"))
);
}
#[test]
fn delimiter_helpers_classify_current_character() {
let space = QueryParser::new(" ");
let newline = QueryParser::new("\n");
assert!(WordComparer::is_block_delimiter(' '));
assert!(WordComparer::is_any_delimiter('.'));
assert!(WordComparer::is_break_line('\n'));
assert!(WordComparer::is_current_block_delimiter(&space));
assert!(WordComparer::is_current_break_line(&newline));
}
#[test]
fn compare_with_block_delimiter_requires_delimited_match() {
assert!(WordComparer::new("id").compare_with_block_delimiter(&QueryParser::new("id,")));
assert!(!WordComparer::new("id").compare_with_block_delimiter(&QueryParser::new("idea")));
}
}