use std::cmp::Ordering;
use lazy_static::lazy_static;
use regex::Regex;
use crate::geo::geodb::GeoDB;
use crate::parser::Parser;
use crate::token::Token;
use crate::NaliText;
lazy_static! {
static ref IPV4_REGEX: Regex = Regex::new(
r"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}"
)
.unwrap();
static ref IPV6_REGEX: Regex = Regex::new(
r"fe80:(:[0-9a-fA-F]{1,4}){0,4}(%\w+)?|([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::[fF]{4}:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}|(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?::(([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})?"
)
.unwrap();
static ref DOMAIN_REGEX: Regex = Regex::new(
r"([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+([a-zA-Z][-a-zA-Z]{0,62})"
)
.unwrap();
}
#[derive(Default)]
pub struct RegexParser {}
impl RegexParser {
fn is_overlapping(start1: usize, end1: usize, start2: usize, end2: usize) -> bool {
start1 < end2 && start2 < end1
}
fn should_keep_first(token1: &Token, start1: usize, token2: &Token, start2: usize) -> bool {
match token1.priority().cmp(&token2.priority()) {
Ordering::Greater => true, Ordering::Less => false, Ordering::Equal => start1 <= start2, }
}
}
impl<G: GeoDB> Parser<G> for RegexParser {
fn name(&self) -> &str {
"regex"
}
fn parse(&self, input: &str, db: &G) -> NaliText {
let mut tokens = Vec::new();
let mut last_end = 0;
let mut matches = Vec::new();
for ip_match in IPV4_REGEX.find_iter(input) {
matches.push((
ip_match.start(),
ip_match.end(),
Token::IPv4(ip_match.as_str().to_string(), db.lookup(ip_match.as_str())),
));
}
for ip_match in IPV6_REGEX.find_iter(input) {
matches.push((
ip_match.start(),
ip_match.end(),
Token::IPv6(ip_match.as_str().to_string(), db.lookup(ip_match.as_str())),
));
}
for domain_match in DOMAIN_REGEX.find_iter(input) {
matches.push((
domain_match.start(),
domain_match.end(),
Token::Domain(domain_match.as_str().to_string()),
));
}
matches.sort_by_key(|(start, _, _)| *start);
let mut filtered_matches = Vec::new();
let mut i = 0;
while i < matches.len() {
let current = &matches[i];
let mut should_add = true;
let mut j = i + 1;
while j < matches.len()
&& Self::is_overlapping(current.0, current.1, matches[j].0, matches[j].1)
{
if !Self::should_keep_first(¤t.2, current.0, &matches[j].2, matches[j].0) {
should_add = false;
break;
}
j += 1;
}
if should_add {
filtered_matches.push(current.clone());
while j < matches.len()
&& Self::is_overlapping(current.0, current.1, matches[j].0, matches[j].1)
{
j += 1;
}
i = j;
} else {
i += 1;
}
}
for (start, end, token) in filtered_matches {
if start > last_end {
tokens.push(Token::Plain(input[last_end..start].to_string()));
}
tokens.push(token);
last_end = end;
}
if last_end < input.len() {
tokens.push(Token::Plain(input[last_end..].to_string()));
}
NaliText::new(tokens)
}
}