use regex::Regex;
#[derive(Clone)]
enum ReSearchType {
Inclusive,
Exclusive,
}
fn str_to_regex(input: &str) -> Result<regex::Regex, regex::Error> {
if input.is_empty() {
return Err(regex::Error::Syntax(
"Can't create an empty regex".to_string(),
));
}
let regex: Regex = Regex::new(format!("^{}$", input.replace('*', ".*")).as_str())?;
Ok(regex)
}
#[macro_export]
macro_rules! matches_optionable_restriction {
($restrict:ident, $($to_check:expr),+) => {
if let Some(restriction) = $restrict {
true $(&& restriction.clone().verify_table_name($to_check))*
} else {
true
}
}
}
#[derive(Clone)]
pub struct Restriction {
regexs: Vec<Regex>,
re_search_type: ReSearchType,
}
impl Restriction {
fn new(re_string: Vec<String>, re_search_type: ReSearchType) -> Restriction {
let mut regexs: Vec<Regex> = Vec::new();
re_string.iter().for_each(|element| {
if let Ok(value) = str_to_regex(element) {
regexs.push(value)
}
});
Restriction {
regexs,
re_search_type,
}
}
pub fn new_inclusion(re_string: Vec<String>) -> Restriction {
Restriction::new(re_string, ReSearchType::Inclusive)
}
pub fn new_exclusion(re_string: Vec<String>) -> Restriction {
Restriction::new(re_string, ReSearchType::Exclusive)
}
pub fn verify_table_name(self, table_name: &str) -> bool {
if !self.regexs.is_empty() {
match self.re_search_type {
ReSearchType::Inclusive => self.regexs.iter().any(|e| e.is_match(table_name)),
ReSearchType::Exclusive => !self.regexs.iter().any(|e| e.is_match(table_name)),
}
} else {
true
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_str_to_regex() {
assert!(str_to_regex("").is_err(), "empty regexs not allowed");
assert_eq!(
str_to_regex("*hell*").unwrap().as_str(),
Regex::new("^.*hell.*$").unwrap().as_str(),
"normal use case"
);
assert_eq!(
str_to_regex("*helO1*").unwrap().as_str(),
Regex::new("^.*helO1.*$").unwrap().as_str(),
"normal use case"
);
assert_eq!(
str_to_regex("*el").unwrap().as_str(),
Regex::new("^.*el$").unwrap().as_str(),
"normal use case"
);
assert_eq!(
str_to_regex("he*").unwrap().as_str(),
Regex::new("^he.*$").unwrap().as_str(),
"normal use case"
);
assert_eq!(
str_to_regex("dollar$*").unwrap().as_str(),
Regex::new("^dollar$.*$").unwrap().as_str(),
"edgy normal use case"
);
}
#[test]
fn test_restrictive_type() {
assert!(
matches!(
{
let rest = Restriction::new_inclusion(vec![String::from(".*hell")]);
rest.re_search_type
},
ReSearchType::Inclusive
),
"normal use case"
);
assert!(
matches!(
{
let rest = Restriction::new_exclusion(vec![String::from(".*hell")]);
rest.re_search_type
},
ReSearchType::Exclusive
),
"normal use case"
);
}
#[test]
fn test_verify_table_name_inclusive() {
assert!(
{
let rest = Restriction::new_inclusion(vec![String::from("hell")]);
rest.verify_table_name("hell")
},
"Exact match"
);
assert!(
{
let rest = Restriction::new_inclusion(vec![String::from("hell*")]);
vec!["hell", "helloe$", "helloa", "hell"]
.iter()
.all(|e| rest.clone().verify_table_name(e))
},
"Exact match"
);
assert!(
!{
let rest = Restriction::new_inclusion(vec![String::from("*ll*")]);
vec!["hel", "heloe$", "heloa", "helel"]
.iter()
.all(|e| rest.clone().verify_table_name(e))
},
"Shouldn't match"
);
assert!(
{
let rest =
Restriction::new_inclusion(vec![String::from("*ll*"), String::from("he*")]);
vec!["hey", "heloe$", "heloa", "helell", "llorn"]
.iter()
.all(|e| rest.clone().verify_table_name(e))
},
"Multiple match and regex"
);
assert!(
{
let rest = Restriction::new_inclusion(vec![]);
vec!["hey", "heloe$", "heloa", "helell", "llorn"]
.iter()
.all(|e| rest.clone().verify_table_name(e))
},
"No regex"
);
}
#[test]
fn test_verify_table_name_exclusive() {
assert!(
!{
let rest = Restriction::new_exclusion(vec![String::from("hell")]);
rest.verify_table_name("hell")
},
"Exact match"
);
assert!(
!{
let rest = Some(Restriction::new_exclusion(vec![String::from("hell*")]));
matches_optionable_restriction!(rest, "hell", "helloe$", "helloa", "hell")
},
"Exact match"
);
assert!(
{
let rest = Restriction::new_exclusion(vec![String::from("*ll*")]);
vec!["hel", "heloe$", "heloa", "helel"]
.iter()
.all(|e| rest.clone().verify_table_name(e))
},
"Shouldn't match"
);
assert!(
!{
let rest =
Restriction::new_exclusion(vec![String::from("*ll*"), String::from("he*")]);
vec!["hey", "heloe$", "heloa", "helell", "llorn"]
.iter()
.all(|e| rest.clone().verify_table_name(e))
},
"Multiple match and regex"
);
assert!(
{
let rest = Restriction::new_exclusion(vec![]);
vec!["hey", "heloe$", "heloa", "helell", "llorn"]
.iter()
.all(|e| rest.clone().verify_table_name(e))
},
"No regex"
);
}
}