pub(super) fn find_balanced_bracket(s: &str) -> usize {
let mut depth = 0i32;
for (i, ch) in s.char_indices() {
match ch {
'[' => depth += 1,
']' => {
if depth == 0 {
return i;
}
depth -= 1;
}
_ => {}
}
}
0
}
pub(super) fn strip_function_body_uses(content: &str) -> String {
let mut result = String::new();
let bytes = content.as_bytes();
let len = bytes.len();
let mut i = 0;
let mut fn_brace_depth: i32 = 0;
while i < len {
if i + 3 <= len
&& &bytes[i..i + 3] == b"fn "
&& (i == 0 || !bytes[i - 1].is_ascii_alphanumeric())
{
result.push_str("fn ");
i += 3;
while i < len {
let ch = bytes[i] as char;
result.push(ch);
i += 1;
if ch == '{' {
fn_brace_depth = 1;
break;
}
}
continue;
}
if fn_brace_depth > 0 {
let ch = bytes[i] as char;
match ch {
'{' => {
fn_brace_depth += 1;
result.push(ch);
i += 1;
}
'}' => {
fn_brace_depth -= 1;
result.push(ch);
i += 1;
}
_ => {
if i + 4 <= len
&& &bytes[i..i + 4] == b"use "
&& (i == 0
|| bytes[i - 1] == b'\n'
|| bytes[i - 1] == b' '
|| bytes[i - 1] == b'\t'
|| bytes[i - 1] == b'{')
{
while i < len && bytes[i] != b';' {
i += 1;
}
if i < len {
i += 1;
}
while i < len && (bytes[i] == b' ' || bytes[i] == b'\n') {
i += 1;
}
} else {
result.push(ch);
i += 1;
}
}
}
} else {
result.push(bytes[i] as char);
i += 1;
}
}
result
}
pub(super) fn strip_cfg_test_modules(content: &str) -> String {
let mut result = String::new();
let mut chars = content.chars().peekable();
let mut in_cfg_test_attr = false;
while let Some(ch) = chars.next() {
if ch == '#' && chars.peek() == Some(&'[') {
let pos = result.len();
result.push(ch);
let mut attr = String::from("#");
for next in chars.by_ref() {
attr.push(next);
if next == ']' {
break;
}
}
result.push_str(&attr[1..]);
let attr_inner = attr.trim();
if attr_inner.starts_with("#[cfg(test)")
|| attr_inner.starts_with("#[cfg(all(") && attr_inner.contains("test")
{
in_cfg_test_attr = true;
result.truncate(pos);
}
continue;
}
if in_cfg_test_attr {
result.push(ch);
if ch.is_whitespace() {
continue;
}
if ch == 'm' {
let mut keyword = String::from("m");
while let Some(&next) = chars.peek() {
if next.is_alphabetic() || next == '_' {
keyword.push(chars.next().unwrap());
} else {
break;
}
}
if keyword == "mod" {
let mut found_end = false;
for next in chars.by_ref() {
if next == ';' {
found_end = true;
break;
}
if next == '{' {
let mut depth = 1;
for inner in chars.by_ref() {
match inner {
'{' => depth += 1,
'}' => {
depth -= 1;
if depth == 0 {
break;
}
}
_ => {}
}
}
found_end = true;
break;
}
}
if found_end {
result.truncate(result.len() - 1); }
in_cfg_test_attr = false;
continue;
} else {
result.push_str(&keyword[1..]); in_cfg_test_attr = false;
}
} else {
in_cfg_test_attr = false;
}
continue;
}
result.push(ch);
}
result
}
pub(super) fn strip_cfg_attributes(s: &str) -> String {
let mut result = String::new();
let mut chars = s.chars().peekable();
while let Some(ch) = chars.next() {
if ch == '#' {
if chars.peek() == Some(&'[') {
chars.next(); let mut depth = 1;
for inner in chars.by_ref() {
match inner {
'[' => depth += 1,
']' => {
depth -= 1;
if depth == 0 {
break;
}
}
_ => {}
}
}
continue;
}
}
result.push(ch);
}
result
}
pub(super) fn strip_comments(content: &str) -> String {
let mut result = String::new();
let mut chars = content.chars().peekable();
while let Some(ch) = chars.next() {
if ch == '/' {
match chars.peek() {
Some('/') => {
chars.next(); while let Some(&next) = chars.peek() {
if next == '\n' {
result.push('\n'); chars.next();
break;
}
chars.next();
}
continue;
}
Some('*') => {
chars.next(); let mut prev_star = false;
for next in chars.by_ref() {
if prev_star && next == '/' {
break;
}
prev_star = next == '*';
if next == '\n' {
result.push('\n');
}
}
continue;
}
_ => {}
}
}
result.push(ch);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_strip_function_body_uses() {
let content = r#"
use crate::types::Foo;
fn bar() {
use std::io;
let x = 1;
}
use crate::types::Bar;
"#;
let stripped = strip_function_body_uses(content);
assert!(stripped.contains("use crate::types::Foo"));
assert!(stripped.contains("use crate::types::Bar"));
assert!(!stripped.contains("use std::io"));
}
#[test]
fn test_strip_cfg_test_modules_inline() {
let content = r#"
use crate::types::Foo;
#[cfg(test)]
mod tests {
use super::*;
fn test_foo() {}
}
fn main() {}
"#;
let stripped = strip_cfg_test_modules(content);
assert!(stripped.contains("use crate::types::Foo"));
assert!(stripped.contains("fn main()"));
assert!(!stripped.contains("mod tests"));
assert!(!stripped.contains("test_foo"));
}
#[test]
fn test_strip_cfg_test_modules_external() {
let content = r#"
use crate::types::Foo;
#[cfg(test)]
mod env_tests;
fn main() {}
"#;
let stripped = strip_cfg_test_modules(content);
assert!(stripped.contains("use crate::types::Foo"));
assert!(stripped.contains("fn main()"));
assert!(!stripped.contains("env_tests"));
}
#[test]
fn test_strip_comments_line() {
let content = "let x = 1; // this is a comment\nlet y = 2;";
let stripped = strip_comments(content);
assert_eq!(stripped, "let x = 1; \nlet y = 2;");
}
#[test]
fn test_strip_comments_block() {
let content = "let x = 1; /* block */ let y = 2;";
let stripped = strip_comments(content);
assert_eq!(stripped, "let x = 1; let y = 2;");
}
#[test]
fn test_strip_cfg_attributes() {
let content = "#[derive(Debug)] struct Foo;";
let stripped = strip_cfg_attributes(content);
assert_eq!(stripped, " struct Foo;");
}
#[test]
fn test_find_balanced_bracket() {
assert_eq!(find_balanced_bracket("foo]"), 3);
assert_eq!(find_balanced_bracket("foo[bar]]"), 8);
assert_eq!(find_balanced_bracket(""), 0);
}
}