ferrous_forge/validation/rust_validator/
patterns.rs

1//! Validation patterns for Rust code
2
3use crate::{Error, Result};
4use regex::Regex;
5
6/// Compiled regex patterns for validation
7pub struct ValidationPatterns {
8    /// Pattern for detecting function definitions
9    pub function_def: Regex,
10    /// Pattern for detecting underscore parameters
11    pub underscore_param: Regex,
12    /// Pattern for detecting underscore in let bindings
13    pub underscore_let: Regex,
14    /// Pattern for detecting unwrap() calls
15    pub unwrap_call: Regex,
16    /// Pattern for detecting expect() calls
17    pub expect_call: Regex,
18}
19
20impl ValidationPatterns {
21    /// Create and compile all validation patterns
22    pub fn new() -> Result<Self> {
23        Ok(Self {
24            function_def: Regex::new(r"^\s*(pub\s+)?(async\s+)?fn\s+")
25                .map_err(|e| Error::validation(format!("Invalid function regex: {}", e)))?,
26
27            underscore_param: Regex::new(r"fn\s+\w+[^{]*\b_\w+\s*:")
28                .map_err(|e| Error::validation(format!("Invalid underscore param regex: {}", e)))?,
29
30            underscore_let: Regex::new(r"^\s*let\s+_\s*=")
31                .map_err(|e| Error::validation(format!("Invalid underscore let regex: {}", e)))?,
32
33            unwrap_call: Regex::new(r"\.unwrap\(\)")
34                .map_err(|e| Error::validation(format!("Invalid unwrap regex: {}", e)))?,
35
36            expect_call: Regex::new(r"\.expect\(")
37                .map_err(|e| Error::validation(format!("Invalid expect regex: {}", e)))?,
38        })
39    }
40}
41
42/// Helper function to check if a pattern is inside a string literal or comment
43pub fn is_in_string_literal(line: &str, pattern: &str) -> bool {
44    // First check if pattern exists at all
45    if !line.contains(pattern) {
46        return false;
47    }
48
49    // Find all occurrences of the pattern
50    let pattern_positions: Vec<usize> = line.match_indices(pattern).map(|(i, _)| i).collect();
51    if pattern_positions.is_empty() {
52        return false;
53    }
54
55    // Check each occurrence to see if it's in a string or comment
56    for pattern_pos in pattern_positions {
57        let mut in_string = false;
58        let mut in_raw_string = false;
59        let mut escaped = false;
60        let mut pos = 0;
61
62        // Check if we're in a comment
63        if let Some(comment_pos) = line.find("//") {
64            if pattern_pos >= comment_pos {
65                continue; // This occurrence is in a comment, check next
66            }
67        }
68
69        for c in line.chars() {
70            if pos >= pattern_pos {
71                // We've reached the pattern position
72                // If we're not in a string, it's a real occurrence
73                if !in_string && !in_raw_string {
74                    return false;
75                }
76                break;
77            }
78
79            if escaped {
80                escaped = false;
81                pos += c.len_utf8();
82                continue;
83            }
84
85            match c {
86                '\\' if in_string && !in_raw_string => escaped = true,
87                '"' if !in_raw_string => in_string = !in_string,
88                'r' if !in_string && !in_raw_string => {
89                    // Check for raw string
90                    let remaining = &line[pos..];
91                    if remaining.starts_with("r\"") || remaining.starts_with("r#\"") {
92                        in_raw_string = true;
93                    }
94                }
95                _ => {}
96            }
97
98            pos += c.len_utf8();
99        }
100    }
101
102    // All occurrences are in strings or comments
103    true
104}