Skip to main content

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