use crate::{Error, Result};
use regex::Regex;
pub struct ValidationPatterns {
pub function_def: Regex,
pub underscore_param: Regex,
pub underscore_let: Regex,
pub unwrap_call: Regex,
pub expect_call: Regex,
}
impl ValidationPatterns {
pub fn new() -> Result<Self> {
Ok(Self {
function_def: Regex::new(r"^\s*(pub\s+)?(async\s+)?fn\s+")
.map_err(|e| Error::validation(format!("Invalid function regex: {}", e)))?,
underscore_param: Regex::new(r"fn\s+\w+[^{]*\b_\w+\s*:")
.map_err(|e| Error::validation(format!("Invalid underscore param regex: {}", e)))?,
underscore_let: Regex::new(r"^\s*let\s+_\s*=")
.map_err(|e| Error::validation(format!("Invalid underscore let regex: {}", e)))?,
unwrap_call: Regex::new(r"\.unwrap\(\)")
.map_err(|e| Error::validation(format!("Invalid unwrap regex: {}", e)))?,
expect_call: Regex::new(r"\.expect\(")
.map_err(|e| Error::validation(format!("Invalid expect regex: {}", e)))?,
})
}
}
pub fn is_in_string_literal(line: &str, pattern: &str) -> bool {
if !line.contains(pattern) {
return false;
}
let pattern_positions: Vec<usize> = line.match_indices(pattern).map(|(i, _)| i).collect();
if pattern_positions.is_empty() {
return false;
}
for pattern_pos in pattern_positions {
let mut in_string = false;
let mut in_raw_string = false;
let mut escaped = false;
let mut pos = 0;
if let Some(comment_pos) = line.find("//")
&& pattern_pos >= comment_pos
{
continue; }
for c in line.chars() {
if pos >= pattern_pos {
if !in_string && !in_raw_string {
return false;
}
break;
}
if escaped {
escaped = false;
pos += c.len_utf8();
continue;
}
match c {
'\\' if in_string && !in_raw_string => escaped = true,
'"' if !in_raw_string => in_string = !in_string,
'r' if !in_string && !in_raw_string => {
let remaining = &line[pos..];
if remaining.starts_with("r\"") || remaining.starts_with("r#\"") {
in_raw_string = true;
}
}
_ => {}
}
pos += c.len_utf8();
}
}
true
}